導讀:java
一、JDK動態代理原理是什麼?爲何不支持類的代理?spring
二、JDK動態代理實例數組
三、CGLib代理原理是什麼?框架
四、CGLib代理實例ide
五、JDK動態代理與CGLib代理的區別是什麼?性能
六、總結測試
注:閱讀本文以前能夠先閱讀:什麼是代理模式? 優化
1. JDK動態代理原理是什麼?爲何不支持類的代理?this
jdk動態代理圖:spa
利用攔截器(攔截器必須實現InvocationHanlder)加上反射機制生成一個實現代理接口的匿名類,在調用具體方法前調用InvokeHandler來處理。是在程序運行的過程當中,根據被代理的接口來動態生成代理類的class文件,並加載運行的過程。
之因此只支持實現了接口的類的代理。從原理上講是由於JVM動態生成的代理類有以下特性:
繼承了Proxy類,實現了代理的接口,最終形式以下(HelloInterface爲被代理類實現的接口):
public final class $Proxy0 extends Proxy implements HelloInterface{ ....... }
然而因爲java不能多繼承,這裏已經繼承了Proxy類了,不能再繼承其餘的類,因此JDK的動態代理不支持對實現類的代理,只支持接口的代理。
從使用上講,建立代理類時必須傳入被代理類實現的接口。
1.1 詳細介紹:
在java的動態代理機制中,有兩個重要的類或接口,一個是 InvocationHandler(Interface)、另外一個則是 Proxy(Class),這一個類和接口是實現咱們動態代理所必須用到的。
1.1.1 InvocationHandler
每個動態代理類都必需要實現InvocationHandler這個接口,而且每一個代理類的實例都關聯了一個handler,當咱們經過代理對象調用一個方法的時候,這個方法的調用就會被轉發爲由InvocationHandler這個接口的 invoke 方法來進行調用。
InvocationHandler這個接口的惟一一個方法 invoke 方法:
Object invoke(Object proxy, Method method, Object[] args) throws Throwable
這個方法一共接受三個參數,那麼這三個參數分別表明以下:
- proxy: 指代JDK動態生成的最終代理對象
- method: 指代的是咱們所要調用真實對象的某個方法的Method對象
- args: 指代的是調用真實對象某個方法時接受的參數
1.1.2 Proxy
Proxy這個類的做用就是用來動態建立一個代理對象的類,它提供了許多的方法,可是咱們用的最多的就是 newProxyInstance 這個方法:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler handler) throws IllegalArgumentException
這個方法的做用就是獲得一個動態的代理對象,其接收三個參數,咱們來看看這三個參數所表明的含義:
- loader: ClassLoader對象,定義了由哪一個ClassLoader來對生成的代理對象進行加載。
- interfaces: Interface對象的數組,表示的是我將要給我須要代理的對象提供一組什麼接口,若是我提供了一組接口給它,那麼這個代理對象就宣稱實現了該接口(多態),這樣我就能調用這組接口中的方法了。
- Handler:InvocationHandler對象,表示的是當我這個動態代理對象在調用方法的時候,會關聯到哪個InvocationHandler對象上。
因此咱們所說的DynamicProxy(動態代理類)是這樣一種class:它是在運行時生成的class,在生成它時你必須提供一組interface給它,而後該class就宣稱它實現了這些 interface。這個DynamicProxy其實就是一個Proxy,它不會作實質性的工做,在生成它的實例時你必須提供一個handler,由它接管實際的工做。
2. JDK動態代理實例
2.1 建立接口類
public interface HelloInterface { void sayHello(); }
2.2 建立被代理類,實現接口
/** * 被代理類 */ public class HelloImpl implements HelloInterface{ @Override public void sayHello() { System.out.println("hello"); } }
2.3建立InvocationHandler實現類
/** * 每次生成動態代理類對象時都須要指定一個實現了InvocationHandler接口的調用處理器對象 */ public class ProxyHandler implements InvocationHandler{ private Object subject; // 這個就是咱們要代理的真實對象,也就是真正執行業務邏輯的類 public ProxyHandler(Object subject) {// 經過構造方法傳入這個被代理對象 this.subject = subject; } /** *當代理對象調用真實對象的方法時,其會自動的跳轉到代理對象關聯的handler對象的invoke方法來進行調用 */ @Override public Object invoke(Object obj, Method method, Object[] objs) throws Throwable { Object result = null; System.out.println("能夠在調用實際方法前作一些事情"); System.out.println("當前調用的方法是" + method.getName()); result = method.invoke(subject, objs);// 須要指定被代理對象和傳入參數 System.out.println(method.getName() + "方法的返回值是" + result); System.out.println("能夠在調用實際方法後作一些事情"); System.out.println("------------------------"); return result;// 返回method方法執行後的返回值 } }
2.4 測試
public class Mytest { public static void main(String[] args) { //第一步:建立被代理對象 HelloImpl hello = new HelloImpl(); //第二步:建立handler,傳入真實對象 ProxyHandler handler = new ProxyHandler(hello); //第三步:建立代理對象,傳入類加載器、接口、handler HelloInterface helloProxy = (HelloInterface) Proxy.newProxyInstance( HelloInterface.class.getClassLoader(), new Class[]{HelloInterface.class}, handler); //第四步:調用方法 helloProxy.sayHello(); } }
2.5 結果
能夠在調用實際方法前作一些事情 當前調用的方法是sayHello hello sayHello方法的返回值是null 能夠在調用實際方法後作一些事情 ------------------------
3. CGLib代理原理是什麼?
CGLib採用了很是底層的字節碼技術,其原理是經過字節碼技術爲一個類建立子類,並在子類中採用方法攔截的技術攔截全部父類方法的調用,順勢織入橫切邏輯。(利用ASM開源包,對代理對象類的class文件加載進來,經過修改其字節碼生成子類來處理)
3.1 CGLib核心類:
一、 net.sf.cglib.proxy.Enhancer:主要加強類,經過字節碼技術動態建立委託類的子類實例;
Enhancer多是CGLIB中最經常使用的一個類,和Java1.3動態代理中引入的Proxy類差很少。和Proxy不一樣的是,Enhancer既可以代理普通的class,也可以代理接口。Enhancer建立一個被代理對象的子類而且攔截全部的方法調用(包括從Object中繼承的toString和hashCode方法)。Enhancer不可以攔截final方法,例如Object.getClass()方法,這是因爲Java final方法語義決定的。基於一樣的道理,Enhancer也不能對fianl類進行代理操做。這也是Hibernate爲何不能持久化final class的緣由。
2、net.sf.cglib.proxy.MethodInterceptor:經常使用的方法攔截器接口,須要實現intercept方法,實現具體攔截處理;
public java.lang.Object intercept(java.lang.Object obj, java.lang.reflect.Method method, java.lang.Object[] args, MethodProxy proxy) throws java.lang.Throwable{}
- obj:動態生成的代理對象
- method : 實際調用的方法
- args:調用方法入參
- proxy:
- net.sf.cglib.proxy.MethodProxy:java Method類的代理類,能夠實現委託類對象的方法的調用;經常使用方法:methodProxy.invokeSuper(proxy, args);在攔截方法內能夠調用屢次
4. CGLib代理實例
4.1 建立被代理類
public class SayHello { public void say(){ System.out.println("hello"); } }
4.2 建立代理類
/** *代理類 */ public class ProxyCglib implements MethodInterceptor{ private Enhancer enhancer = new Enhancer(); public Object getProxy(Class clazz){ //設置須要建立子類的類 enhancer.setSuperclass(clazz); enhancer.setCallback(this); //經過字節碼技術動態建立子類實例 return enhancer.create(); } //實現MethodInterceptor接口方法 public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("能夠在調用實際方法前作一些事情"); //經過代理類調用父類中的方法 Object result = proxy.invokeSuper(obj, args); System.out.println("能夠在調用實際方法後作一些事情"); return result; } }
4.3 測試
public class Mytest { public static void main(String[] args) { ProxyCglib proxy = new ProxyCglib(); //經過生成子類的方式建立代理類 SayHello proxyImp = (SayHello)proxy.getProxy(SayHello.class); proxyImp.say(); } }
4.4 結果
能夠在調用實際方法前作一些事情 hello 能夠在調用實際方法後作一些事情
5. JDK動態代理與CGLib代理的區別是什麼?
5.1 原理區別:
java動態代理是利用反射機制生成一個實現代理接口的匿名類,在調用具體方法前調用InvokeHandler來處理。核心是實現InvocationHandler接口,使用invoke()方法進行面向切面的處理,調用相應的通知。
而cglib動態代理是利用asm開源包,對代理對象類的class文件加載進來,經過修改其字節碼生成子類來處理。核心是實現MethodInterceptor接口,使用intercept()方法進行面向切面的處理,調用相應的通知。
1、若是目標對象實現了接口,默認狀況下會採用JDK的動態代理實現AOP
2、若是目標對象實現了接口,能夠強制使用CGLIB實現AOP
3、若是目標對象沒有實現了接口,必須採用CGLIB庫,spring會自動在JDK動態代理和CGLIB之間轉換
5.2 性能區別:
1、CGLib底層採用ASM字節碼生成框架,使用字節碼技術生成代理類,在jdk6以前比使用Java反射效率要高。惟一須要注意的是,CGLib不能對聲明爲final的方法進行代理,由於CGLib原理是動態生成被代理類的子類。
2、在jdk6、jdk7、jdk8逐步對JDK動態代理優化以後,在調用次數較少的狀況下,JDK代理效率高於CGLIB代理效率,只有當進行大量調用的時候,jdk6和jdk7比CGLIB代理效率低一點,可是到jdk8的時候,jdk代理效率高於CGLIB代理。
5.3 各自侷限:
1、JDK的動態代理機制只能代理實現了接口的類,而不能實現接口的類就不能實現JDK的動態代理。
2、cglib是針對類來實現代理的,他的原理是對指定的目標類生成一個子類,並覆蓋其中方法實現加強,但由於採用的是繼承,因此不能對final修飾的類進行代理。
6. 總結
擴展閱讀:動態代理是基於什麼原理?