靜態代理&動態代理


代理的核心就是攔截方法調用,並在需要的時候執行匹配某方法的通知鏈。 

和CGLIB不同的是,JDK代理只能代理接口,不能代理類。 

使用JDK代理時,如何處理一個特定的方法調用的決定是在程序運行時做出的,也就是在每次方法被調用時。使用CGLIB代理可以邊開這種處理方法,CGLIB會在運行中隨時爲代理創建新類的字節碼,並儘可能的重用已經生成的類的字節碼。 


Spring1.2:
將事務代理工廠[TransactionProxyFactoryBean] 或 自動代理攔截器[BeanNameAutoProxyCreator]
的 proxyTargetClass 屬性,設置爲true,則使用CGLIB代理,此屬性默認爲false,使用JDK動態代理.
以下引用 Spring Framework reference 2.0.5:
Spring2.0:

spring AOP部分使用JDK動態代理或者CGLIB來爲目標對象創建代理。(建議儘量使用JDK的動態代理)

如果被代理的目標對象實現了至少一個接口,則會使用JDK動態代理。所有該目標類型實現的接口都將被代理。若該目標對象沒有實現任何接口,則創建一個CGLIB代理。

如果你希望強制使用CGLIB代理,(例如:希望代理目標對象的所有方法,而不只是實現自接口的方法)那也可以。但是需要考慮以下問題:

無法通知(advise)Final 方法,因爲他們不能被覆寫。 
你需要將CGLIB 2二進制發行包放在classpath下面,與之相較JDK本身就提供了動態代理 
強制使用CGLIB代理需要將 |aop:config| 的 proxy-target-class 屬性設爲true:

|aop:config proxy-target-class="true"|
...
|/aop:config|

當需要使用CGLIB代理和@AspectJ自動代理支持,請按照如下的方式設置 |aop:aspectj-autoproxy| 的 proxy-target-class 屬性:

|aop:aspectj-autoproxy proxy-target-class="true"/|

而實際使用的過程中才會發現細節問題的差別,The devil is in the detail.JDK動態代理:其代理對象必須是某個接口的實現,它是通過在運行期間創建一個接口的實現類來完成對目標對象的代理。
CGLIB代理:實現原理類似於JDK動態代理,只是它在運行期間生成的代理對象是針對目標類擴展的子類。CGLIB是高效的代碼生成包,底層是依靠ASM(開源的Java字節碼編輯類庫)操作字節碼實現的,性能比JDK強。
Spring是依靠什麼來判斷採用哪種代理策略來生成AOP代理呢?以下代碼就是Spring的判斷邏輯  

    //org.springframework.aop.framework.DefaultAopProxyFactory   
    
//參數AdvisedSupport 是Spring AOP配置相關類   
    public AopProxy createAopProxy(AdvisedSupport advisedSupport)   
            
throws AopConfigException {   
        
//在此判斷使用JDK動態代理還是CGLIB代理   
        if (advisedSupport.isOptimize() || advisedSupport.isProxyTargetClass()   
                
|| hasNoUserSuppliedProxyInterfaces(advisedSupport)) {   
            
if (!cglibAvailable) {   
                
thrownew AopConfigException(   
                        
"Cannot proxy target class because CGLIB2 is not available. "  
                                
+"Add CGLIB to the class path or specify proxy interfaces.");   
              }   
            
return CglibProxyFactory.createCglibProxy(advisedSupport);   
          } 
else {   
            
returnnew JdkDynamicAopProxy(advisedSupport);   
          }   
      }  

advisedSupport.isOptimize()與advisedSupport.isProxyTargetClass()默認返回都是false,所以在默認情況下目標對象有沒有實現接口決定着Spring採取的策略,當然可以設置advisedSupport.isOptimize()或者advisedSupport.isProxyTargetClass()返回爲true,這樣無論目標對象有沒有實現接口Spring都會選擇使用CGLIB代理。

所以在默認情況下,如果一個目標對象如果實現了接口Spring則會選擇JDK動態代理策略動態的創建一個接口實現類(動態代理類)來代理目標對象,可以通俗的理解這個動態代理類是目標對象的另外一個版本,所以這兩者之間在強制轉換的時候會拋出java.lang.ClassCastException。而所以在默認情況下,如果目標對象沒有實現任何接口,Spring會選擇CGLIB代理, 其生成的動態代理對象是目標類的子類。

上說的是默認情況下,也可以手動配置一些選項使Spring採用CGLIB代理。 
org.springframework.transaction.interceptor.TransactionProxyFactoryBean是org.springframework.aop.framework. ProxyConfig的子類,所以可以參照ProxyConfig裏的一些設置如下所示,將optimize和proxyTargetClass任意一個設置爲true都可以強制Spring採用CGLIB代理。

如果當需要使用CGLIB代理和@AspectJ自動代理支持,請按照如下的方式設置 |aop:aspectj-autoproxy| 的 proxy-target-class 屬性: 
|aop:aspectj-autoproxy proxy-target-class="true"/|

這樣使用CGLIB代理也就不會出現前面提到的ClassCastException問題了,也可以在性能上有所提高,關鍵是對於代理對象是否繼承接口可以統一使用。





兩種類型AOP:靜態AOP和動態AOP。 

靜態代理: 
代理對象與被代理對象必須實現同一個接口。
 
demo:

  1. package cn.partner4java.proxy.staticproxy;  
  2.   
  3.   
  4. /** 
  5. * 靜態代理,統一接口 
  6. * @author partner4java 
  7. * 
  8. */  
  9. public interface IHello {  
  10.     /** 
  11.      * 可以帶來的統一方法 
  12.      * @param name 
  13.      */  
  14.     public void hello(String name);  
  15. }  
  1. package cn.partner4java.proxy.staticproxy;  
  2.   
  3. /** 
  4. * 被代理的對象,需要藉助代理對象加入日誌 
  5. * @author partner4java 
  6. * 
  7. */  
  8. public class HelloSpeaker implements IHello {  
  9.   
  10.     public void hello(String name) {  
  11.         System.out.println("Hello " + name);  
  12.     }  
  13.   
  14. }  
  1. package cn.partner4java.proxy.staticproxy;  
  2.   
  3. /** 
  4. * 代理對象,給被代理對象添加日誌 
  5. */  
  6. public class HelloProxy implements IHello {  
  7.       
  8.     private IHello iHello;  
  9.   
  10.     public HelloProxy(IHello iHello) {  
  11.         super();  
  12.         this.iHello = iHello;  
  13.     }  
  14.   
  15.   
  16.     public void hello(String name) {  
  17.         System.out.println("記錄日誌");  
  18.         iHello.hello(name);  
  19.     }  
  20.   
  21. }  
  1. package cn.partner4java.proxy.staticproxy;  
  2.   
  3. /** 
  4. * 調用 
  5. * @author partner4java 
  6. * 
  7. */  
  8. public class ProxyDemo {  
  9.   
  10.     public static void main(String[] args) {  
  11.         IHello iHello = new HelloProxy(new HelloSpeaker());  
  12.         iHello.hello("long");  
  13.     }  
  14.   
  15. }  





動態代理: 
動態代理區別於靜態帶來實現的地方在於織入過程是在運行時動態進行的。自己實現一般實現java.lang.reflect.InvocationHandler接口。 
例子:
  1. package cn.partner4java.proxy.dynamicproxy;  
  2.   
  3.   
  4. public interface IHello {  
  5.     public void hello(String name);  
  6. }  
  1. package cn.partner4java.proxy.dynamicproxy;  
  2.   
  3. /** 
  4. * 被代理的對象,需要藉助代理對象加入日誌 
  5. * @author partner4java 
  6. * 
  7. */  
  8. public class HelloSpeaker implements IHello {  
  9.   
  10.     public void hello(String name) {  
  11.         System.out.println("Hello " + name);  
  12.     }  
  13.   
  14. }  
  1. package cn.partner4java.proxy.dynamicproxy;  
  2.   
  3. import java.lang.reflect.InvocationHandler;  
  4. import java.lang.reflect.Method;  
  5. import java.lang.reflect.Proxy;  
  6.   
  7. /** 
  8. * 動態代理對象 
  9. * @author partner4java 
  10. * 
  11. */  
  12. public class LogHandler implements InvocationHandler {  
  13.   
  14.     private Object delegate;  
  15.       
  16.     public Object bind(Object delegate){  
  17.         this.delegate = delegate;  
  18.         return Proxy.newProxyInstance(delegate.getClass().getClassLoader(),   
  19.                 delegate.getClass().getInterfaces(), this);  
  20.     }  
  21.     /** 
  22.      * 代理對象,這裏面還可以改變原有的方法 
  23.      */  
  24.     public Object invoke(Object proxy, Method method, Object[] args)  
  25.             throws Throwable {  
  26.         Object result = null;  
  27.         try {  
  28.             System.out.println("添加日誌");  
  29.             result = method.invoke(delegate, args);  
  30.         } catch (Exception e) {  
  31.             e.printStackTrace();  
  32.         }  
  33.           
  34.         return null;  
  35.     }  
  36.   
  37. }  
  1. package cn.partner4java.proxy.dynamicproxy;  
  2.   
  3. /** 
  4. * 測試 
  5. * @author partner4java 
  6. * 
  7. */  
  8. public class ProxyDemo {  
  9.     public static void main(String[] args) {  
  10.         LogHandler logHandler = new LogHandler();  
  11.         IHello iHello = (IHello) logHandler.bind(new HelloSpeaker());  
  12.         iHello.hello("long");  
  13.     }  
  14. }  


------------------------------------------------------------------ 


利用ProxyFactory連接CGLIB簡單實現AOP: 
加入包aopalliance.jar\cglib-nodep-2.1_3.jar 
demo:
  1. package cn.partner4java.proxy.proxyfactory;  
  2.   
  3. /** 
  4. * 被代理的對象 
  5. * @author partner4java 
  6. * 
  7. */  
  8. public class MessageWriter {  
  9.     public void writeMessage(){  
  10.         System.out.println("world!");  
  11.     }  
  12. }  
  1. package cn.partner4java.proxy.proxyfactory;  
  2.   
  3. import org.aopalliance.intercept.MethodInterceptor;  
  4. import org.aopalliance.intercept.MethodInvocation;  
  5.   
  6. /** 
  7. * 裝飾者<br/> 
  8. * MethodInterceptor接口是對方法調用連接點實現包圍通知的AOP聯盟標準接口 
  9. * @author partner4java 
  10. * 
  11. */  
  12. public class MessageDecorator implements MethodInterceptor{  
  13.   
  14.     public Object invoke(MethodInvocation invocation) throws Throwable {  
  15.         System.out.print("Hello ");  
  16.         Object retVal = invocation.proceed();  
  17.         return retVal;  
  18.     }  
  19.   
  20. }  
  1. package cn.partner4java.proxy.proxyfactory;  
  2.   
  3. import org.springframework.aop.framework.ProxyFactory;  
  4.   
  5. /** 
  6. * 調用組裝 
  7. * 這裏最重要的部分是我們使用ProxyFactory來創建一個目標對象代理,同時織入通知  
  8. * @author partner4java 
  9. * 
  10. */  
  11. public class HelloWorldWeaver {  
  12.   
  13.     public static void main(String[] args) {  
  14.         //目標  
  15.         MessageWriter target = new MessageWriter();  
  16.           
  17.         //create the proxy  
  18.         ProxyFactory proxyFactory = new ProxyFactory();  
  19.           
  20.         proxyFactory.addAdvice(new MessageDecorator());  
  21.         proxyFactory.setTarget(target);  
  22.           
  23.         //獲取返回被代理的目標  
  24.         MessageWriter proxy = (MessageWriter) proxyFactory.getProxy();  
  25.           
  26.         target.writeMessage();  
  27.         System.out.println("---");  
  28.         proxy.writeMessage();  
  29. //      後臺打印:  
  30. //      world!  
  31. //      ---  
  32. //      World world!  
  33.     }  
  34.   
  35. }  
發佈了19 篇原創文章 · 獲贊 14 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章