AOP的底層實現-CGLIB動態代理和JDK動態代理 AOP的底層實現-CGLIB動態代理和JDK動態代理

AOP的底層實現-CGLIB動態代理和JDK動態代理

cglib的實現原理   :在內存爲目標對象創建一個子類對象,通過覆寫父類的方法 ,實現功能增強。

AOP是目前Spring框架中的核心之一,在應用中具有非常重要的作用,也是Spring其他組件的基礎。它是一種面向切面編程的思想。關於AOP的基礎知識,相信多數童鞋都已經瞭如指掌,我們就略過這部分,來講解下AOP的核心功能的底層實現機制:如何用動態代理來實現切面攔截。

        AOP的攔截功能是由java中的動態代理來實現的。說白了,就是在目標類的基礎上增加切面邏輯,生成增強的目標類(該切面邏輯或者在目標類函數執行之前,或者目標類函數執行之後,或者在目標類函數拋出異常時候執行。不同的切入時機對應不同的Interceptor的種類,如BeforeAdviseInterceptor,AfterAdviseInterceptor以及ThrowsAdviseInterceptor等)。
        那麼動態代理是如何實現將切面邏輯(advise)織入到目標類方法中去的呢?下面我們就來詳細介紹並實現AOP中用到的兩種動態代理。
        AOP的源碼中用到了兩種動態代理來實現攔截切入功能:jdk動態代理和cglib動態代理。兩種方法同時存在,各有優劣。jdk動態代理是由java內部的反射機制來實現的,cglib動態代理底層則是藉助asm來實現的。總的來說,反射機制在生成類的過程中比較高效,而asm在生成類之後的相關執行過程中比較高效(可以通過將asm生成的類進行緩存,這樣解決asm生成類過程低效問題)。還有一點必須注意:jdk動態代理的應用前提,必須是目標類基於統一的接口。如果沒有上述前提,jdk動態代理不能應用。由此可以看出,jdk動態代理有一定的侷限性,cglib這種第三方類庫實現的動態代理應用更加廣泛,且在效率上更有優勢。
一、jdk動態代理實現AOP攔截(代碼中的關鍵地方都添加了註釋)

1、爲目標類(target)定義統一的接口類Service,這個是jdk動態代理必須的前提。
[java] view plain copy
  1. package jdkproxy;  
  2.   
  3. /** 
  4.  * 該類是所有被代理類的接口類,jdk實現的代理要求被代理類基於統一的接口 
  5.  *  
  6.  * @author typ 
  7.  *  
  8.  */  
  9. public interface Service {  
  10.     /** 
  11.      * add方法 
  12.      */  
  13.     public void add();  
  14.   
  15.     /** 
  16.      * update方法 
  17.      */  
  18.     public void update();  
  19. }  
2、目標類AService,我們的實驗目標就是在AService中add和update方法的前後實現攔截,加入自定義切面邏輯advise
[java] view plain copy
  1. package jdkproxy;  
  2.   
  3. /** 
  4.  * 被代理類,即目標類target 
  5.  *  
  6.  * @author typ 
  7.  *  
  8.  */  
  9. public class AService implements Service {  
  10.     /* 
  11.      * (non-Javadoc) 
  12.      *  
  13.      * @see jdkproxy.Service#add() 
  14.      */  
  15.     public void add() {  
  16.         System.out.println("AService add>>>>>>>>>>>>>>>>>>");  
  17.     }  
  18.   
  19.     /* 
  20.      * (non-Javadoc) 
  21.      *  
  22.      * @see jdkproxy.Service#update() 
  23.      */  
  24.     public void update() {  
  25.         System.out.println("AService update>>>>>>>>>>>>>>>");  
  26.     }  
  27. }  
3、實現動態代理類MyInvocationHandler,實現InvocationHandler接口,並且實現接口中的invoke方法。仔細看invoke方法,就是在該方法中加入切面邏輯的。目標類方法的執行是由mehod.invoke(target,args)這條語句完成。
[java] view plain copy
  1. package jdkproxy;  
  2.   
  3. import java.lang.reflect.InvocationHandler;  
  4. import java.lang.reflect.Method;  
  5.   
  6. /** 
  7.  * @author typ 
  8.  * 
  9.  */  
  10. public class MyInvocationHandler implements InvocationHandler {  
  11.     private Object target;  
  12.   
  13.     MyInvocationHandler() {  
  14.         super();  
  15.     }  
  16.   
  17.     MyInvocationHandler(Object target) {  
  18.         super();  
  19.         this.target = target;  
  20.     }  
  21.   
  22.     public Object invoke(Object proxy, Method method, Object[] args)  
  23.             throws Throwable {  
  24.         // 程序執行前加入邏輯,MethodBeforeAdviceInterceptor  
  25.         System.out.println("before-----------------------------");  
  26.         // 程序執行  
  27.         Object result = method.invoke(target, args);  
  28.         // 程序執行後加入邏輯,MethodAfterAdviceInterceptor  
  29.         System.out.println("after------------------------------");  
  30.         return result;  
  31.     }  
  32.   
  33. }  
4、測試類,其中增強的目標對象是由Proxy.newProxyInstance(aService.getClass().getClassLoader(), aService.getClass().getInterfaces(), handler);來生成的。
[java] view plain copy
  1. package jdkproxy;  
  2.   
  3. import java.lang.reflect.Proxy;  
  4.   
  5. /** 
  6.  * @author typ 
  7.  * 
  8.  */  
  9. public class Test {  
  10.     public static void main(String[] args) {  
  11.         Service aService = new AService();  
  12.         MyInvocationHandler handler = new MyInvocationHandler(aService);  
  13.         // Proxy爲InvocationHandler實現類動態創建一個符合某一接口的代理實例  
  14.         Service aServiceProxy = (Service) Proxy.newProxyInstance(aService  
  15.                 .getClass().getClassLoader(), aService.getClass()  
  16.                 .getInterfaces(), handler);  
  17.         // 由動態生成的代理對象來aServiceProxy 代理執行程序,其中aServiceProxy 符合Service接口  
  18.         aServiceProxy.add();  
  19.         System.out.println();  
  20.         aServiceProxy.update();  
  21.         // 以下是對B的代理  
  22.         // Service bService = new BService();  
  23.         // MyInvocationHandler handler = new MyInvocationHandler(bService);  
  24.         // Service bServiceProxy = (Service) Proxy.newProxyInstance(bService  
  25.         // .getClass().getClassLoader(), bService.getClass()  
  26.         // .getInterfaces(), handler);  
  27.         // bServiceProxy.add();  
  28.         // System.out.println();  
  29.         // bServiceProxy.update();  
  30.     }  
  31. }  
自此,jdk動態代理來實現AOP攔截機制的代碼已經實現,下面我們看一下攔截的結果,程序輸出結果如下:
before-----------------------------
AService add>>>>>>>>>>>>>>>>>>
after------------------------------
before-----------------------------
AService update>>>>>>>>>>>>>>>
after------------------------------
可以看到,在目標類AService的add和update方法前後已經加入了自定義的切面邏輯,AOP攔截機制生效了。

二、cglib動態代理實現AOP攔截(代碼中的關鍵地方都添加了註釋

1、目標類,cglib不需要定義目標類的統一接口
[java] view plain copy
  1. package cglibproxy;  
  2.   
  3. /** 
  4.  * 被代理類,即目標對象target 
  5.  *  
  6.  * @author typ 
  7.  *  
  8.  */  
  9. public class Base {  
  10.     /** 
  11.      * 一個模擬的add方法 
  12.      */  
  13.     public void add() {  
  14.         System.out.println("add ------------");  
  15.     }  
  16. }  
2、實現動態代理類CglibProxy,需要實現MethodInterceptor接口,實現intercept方法。該代理中在add方法前後加入了自定義的切面邏輯,目標類add方法執行語句爲proxy.invokeSuper(object, args);
[java] view plain copy
  1. package cglibproxy;  
  2.   
  3. import java.lang.reflect.Method;  
  4. import net.sf.cglib.proxy.MethodInterceptor;  
  5. import net.sf.cglib.proxy.MethodProxy;  
  6.   
  7. /** 
  8.  * 此爲代理類,用於在pointcut處添加advise 
  9.  *  
  10.  * @author typ 
  11.  *  
  12.  */  
  13. public class CglibProxy implements MethodInterceptor {  
  14.   
  15.     public Object intercept(Object object, Method method, Object[] args,  
  16.             MethodProxy proxy) throws Throwable {  
  17.         // 添加切面邏輯(advise),此處是在目標類代碼執行之前,即爲MethodBeforeAdviceInterceptor。  
  18.         System.out.println("before-------------");  
  19.         // 執行目標類add方法  
  20.         proxy.invokeSuper(object, args);  
  21.         // 添加切面邏輯(advise),此處是在目標類代碼執行之後,即爲MethodAfterAdviceInterceptor。  
  22.         System.out.println("after--------------");  
  23.         return null;  
  24.     }  
  25.   
  26. }  
3、獲取增強的目標類的工廠Factory,其中增強的方法類對象是有Enhancer來實現的,代碼如下所示:
[java] view plain copy
  1. package cglibproxy;  
  2.   
  3. import net.sf.cglib.proxy.Enhancer;  
  4.   
  5. /** 
  6.  * 工廠類,生成增強過的目標類(已加入切入邏輯) 
  7.  *  
  8.  * @author typ 
  9.  *  
  10.  */  
  11. public class Factory {  
  12.     /** 
  13.      * 獲得增強之後的目標類,即添加了切入邏輯advice之後的目標類 
  14.      *  
  15.      * @param proxy 
  16.      * @return 
  17.      */  
  18.     public static Base getInstance(CglibProxy proxy) {  
  19.         Enhancer enhancer = new Enhancer();  
  20.         enhancer.setSuperclass(Base.class);  
  21.         //回調方法的參數爲代理類對象CglibProxy,最後增強目標類調用的是代理類對象CglibProxy中的intercept方法  
  22.         enhancer.setCallback(proxy);  
  23.         // 此刻,base不是單純的目標類,而是增強過的目標類  
  24.         Base base = (Base) enhancer.create();  
  25.         return base;  
  26.     }  
  27. }  
4、測試類
[java] view plain copy
  1. package cglibproxy;  
  2.   
  3. /** 
  4.  * @author typ 
  5.  * 
  6.  */  
  7. public class Test {  
  8.     public static void main(String[] args) {  
  9.         CglibProxy proxy = new CglibProxy();  
  10.         // base爲生成的增強過的目標類  
  11.         Base base = Factory.getInstance(proxy);  
  12.         base.add();  
  13.     }  
  14. }  
自此,cglib動態代理實現的AOP攔截機制已經基本實現,下面我們來看一下攔截的效果如何,程序執行結果如下:
before-------------
add ------------
after--------------
可以看到,在目標類Base的add方法前後已經加入了自定義的切面邏輯,AOP攔截機制生效了。
此外,需要說明一下的是,cglib動態代理用到了第三方類庫,需要在項目中引入兩個jar包:cglib.jar和asm.jar。稍後會在csdn資源中上傳這兩個jar包。免積分的啊。

總之,AOP的核心機制和基本功能已經能夠通過動態代理來實現了,至於AOP中,如何從配置文檔中得到目標類target、advisor的bean,如何判斷攔截器類型等問題,就藉助於Spring中另一個核心IOC來解決了,後續會有IOC的核心實現機制講解。
個人分類: JAVA
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章