原文地址:https://my.oschina.net/huangyong/blog/159788
做者:黃勇
黃勇,從事近十年的 JavaEE 應用開發工做,曾任阿里巴巴公司系統架構師,現任特贊CTO。對分佈式服務架構與大數據技術有深刻研究,具備豐富的 B/S 架構開發經驗與項目實戰經驗,擅長敏捷開發模式。國內開源軟件推進者之一,Smart Framework 開源框架創始人。熱愛技術交流,樂於分享本身的工做經驗。目前著有《架構探險——從零開始寫Java Web框架》和《架構探險-輕量級微服務架構.上冊》兩本書,下半年即將出版《架構探險-輕量級微服務架構.下冊》。java
在前面的文章:jdk的動態代理,介紹了JDK
動態代理,用了這個DynamicProxy
之後,以爲它仍是很是好的,好的地方是,接口變了,這個動態代理類不用作改動。而靜態代理就不同了,接口變了,實現類還須要動,代理類也須要動。可是JDK
動態代理也並非」萬靈丹」,也有侷限性,它也有搞不定的時候,好比要代理一個沒有任何接口的類,它就沒有用武之地了。架構
那麼,可否代理沒有接口的類呢?答案是確定的,那就是CGlib
這個類庫。雖然它看起來不太起眼,可是Spring
,Hibernate
這樣高端的開源框架都用到了它。它是一個在運行期間動態生成字節碼的工具,也就是動態生成代理類了。提及來很高深,實際用起來一點也不難,下面再寫一個CGlibProxy
。框架
我使用的maven
工程進行構建,先引入依賴:maven
<dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.1</version> </dependency>
編寫CGlibProxy
:分佈式
package org.light4j.proxy.dynamicProxy; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; public class CGLibProxy implements MethodInterceptor { @SuppressWarnings("unchecked") public <T> T getProxy(Class<T> cls){ return (T) Enhancer.create(cls, this); } @Override public Object intercept(Object obj, Method method, Object[] arg, MethodProxy proxy) throws Throwable { before(); Object result = proxy.invokeSuper(obj, arg); after(); return result; } private void before() { System.out.println("Before..."); } private void after() { System.out.println("After..."); } }
須要實現CGLib
給咱們提供的MethodInterceptor
接口,並填充intercept
方法。方法中最後一個MethodProxy
類型的參數proxy
值得注意,CGLib
給咱們提供的是方法級別的代理,也能夠理解爲對方法的攔截(這不就是傳說中的」方法攔截器」嗎?)。這個功能對於咱們來講,如同雪中送炭。咱們直接調用proxy
的invokeSuper
方法,將被代理的對象obj
以及方法參數args
傳入其中便可。ide
與DynamicProxy
相似,在CGLibProxy
中也添加一個泛型的getProxy
方法,便於咱們能夠快速地獲取自動生成的代理對象。下面用一個main
方法來描述:微服務
package org.light4j.proxy; import org.light4j.proxy.dynamicProxy.CGLibProxy; import org.light4j.proxy.impl.HelloImpl; public class Main { public static void main(String[] args) { CGLibProxy cgLibProxy = new CGLibProxy(); Hello helloProxy = cgLibProxy.getProxy(HelloImpl.class); helloProxy.say("longjiazuo"); } }
仍然經過兩行代碼就能夠返回代理對象,與JDK動態代理不一樣的是,這裏不須要任何的接口信息,對誰均可以生成動態代理對象(無論它是」矮窮挫」,仍是」高富帥」)。工具
因爲我一貫都追求完美,用兩行代碼返回代理對象仍是有些多餘的,不想老是去new這個CGLibProxy
對象,最好new
一次,之後隨時拿隨時用,因而想到了」單例模式」:大數據
package org.light4j.proxy.dynamicProxy; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; public class CGLibProxy implements MethodInterceptor { private static CGLibProxy instance = new CGLibProxy(); private CGLibProxy(){ } public static CGLibProxy getInstance(){ return instance; } @SuppressWarnings("unchecked") public <T> T getProxy(Class<T> cls){ return (T) Enhancer.create(cls, this); } @Override public Object intercept(Object obj, Method method, Object[] arg, MethodProxy proxy) throws Throwable { before(); Object result = proxy.invokeSuper(obj, arg); after(); return result; } private void before() { System.out.println("Before..."); } private void after() { System.out.println("After..."); } }
加上了以上幾行代碼就解決問題了,須要說明的是,這裏有一個private
的構造方法,就是爲了限制外界不能再去new
它了,換句話說,這個類被」閹割」了。this
下面用一個main
方法來證實個人簡單主義思想:
package org.light4j.proxy; import org.light4j.proxy.dynamicProxy.CGLibProxy; import org.light4j.proxy.impl.HelloImpl; public class Main { public static void main(String[] args) { Hello helloProxy = CGLibProxy.getInstance().getProxy(HelloImpl.class); helloProxy.say("longjiazuo"); } }
沒錯,只需一行代碼就能夠獲取代理對象了!
到此,經過三篇文章的分析,前後介紹了無代理,靜態代理,JDK
動態代理,CGLib
動態代理,其實代理的世界遠不止這麼小,還有不少實際的應用場景。
摘自 http://blog.longjiazuo.com/archives/1851