1. 程式人生 > >Java 動態代理作用是什麼 ==AOP?

Java 動態代理作用是什麼 ==AOP?

之前雖然會用JDK的動態代理,但是有些問題卻一直沒有搞明白。比如說:InvocationHandler的invoke方法是由誰來呼叫的,代理物件是怎麼生成的,直到前幾個星期才把這些問題全部搞明白了。 
    廢話不多說了,先來看一下JDK的動態是怎麼用的。 

Java程式碼  收藏程式碼
  1. package dynamic.proxy;   
  2. import java.lang.reflect.InvocationHandler;  
  3. import java.lang.reflect.Method;  
  4. import java.lang.reflect.Proxy;  
  5. /**
     
  6.  * 實現自己的InvocationHandler 
  7.  * @author zyb 
  8.  * @since 2012-8-9 
  9.  * 
  10.  */  
  11. public class MyInvocationHandler implements InvocationHandler {  
  12.     // 目標物件   
  13.     private Object target;  
  14.     /** 
  15.      * 構造方法 
  16.      * @param target 目標物件  
  17.      */  
  18.     public MyInvocationHandler(Object target) {  
  19.         super();  
  20.         this.target = target;  
  21.     }  
  22.     /** 
  23.      * 執行目標物件的方法 
  24.      */  
  25.     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {  
  26.         // 在目標物件的方法執行之前簡單的列印一下  
  27.         System.out.println("------------------before------------------");  
  28.         // 執行目標物件的方法  
  29.         Object result = method.invoke(target, args);  
  30.         // 在目標物件的方法執行之後簡單的列印一下  
  31.         System.out.println("-------------------after------------------");  
  32.         return result;  
  33.     }  
  34.     /** 
  35.      * 獲取目標物件的代理物件 
  36.      * @return 代理物件 
  37.      */  
  38.     public Object getProxy() {  
  39.         return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),   
  40.                 target.getClass().getInterfaces(), this);  
  41.     }  
  42. }  
  43. package dynamic.proxy;  
  44. /** 
  45.  * 目標物件實現的介面,用JDK來生成代理物件一定要實現一個介面 
  46.  * @author zyb 
  47.  * @since 2012-8-9 
  48.  * 
  49.  */  
  50. public interface UserService {  
  51.     /** 
  52.      * 目標方法  
  53.      */  
  54.     public abstract void add();  
  55. }  
  56. package dynamic.proxy;   
  57. /** 
  58.  * 目標物件 
  59.  * @author zyb 
  60.  * @since 2012-8-9 
  61.  * 
  62.  */  
  63. public class UserServiceImpl implements UserService {  
  64.     /* (non-Javadoc) 
  65.      * @see dynamic.proxy.UserService#add() 
  66.      */  
  67.     public void add() {  
  68.         System.out.println("--------------------add---------------");  
  69.     }  
  70. }  
  71. package dynamic.proxy;   
  72. import org.junit.Test;  
  73. /** 
  74.  * 動態代理測試類 
  75.  * @author zyb 
  76.  * @since 2012-8-9 
  77.  * 
  78.  */  
  79. public class ProxyTest {  
  80.     @Test  
  81.     public void testProxy() throws Throwable {  
  82.         // 例項化目標物件  
  83.         UserService userService = new UserServiceImpl();  
  84.         // 例項化InvocationHandler  
  85.         MyInvocationHandler invocationHandler = new MyInvocationHandler(userService);  
  86.         // 根據目標物件生成代理物件  
  87.         UserService proxy = (UserService) invocationHandler.getProxy();  
  88.         // 呼叫代理物件的方法  
  89.         proxy.add();  
  90.     }  
  91. }  


執行結果如下: 
------------------before------------------ 
--------------------add--------------- 
-------------------after------------------


   用起來是很簡單吧,其實這裡基本上就是AOP的一個簡單實現了,在目標物件的方法執行之前和執行之後進行了增強。Spring的AOP實現其實也是用了Proxy和InvocationHandler這兩個東西的。 

    用起來是比較簡單,但是如果能知道它背後做了些什麼手腳,那就更好不過了。首先來看一下JDK是怎樣生成代理物件的。既然生成代理物件是用的Proxy類的靜態方newProxyInstance,那麼我們就去它的原始碼裡看一下它到底都做了些什麼? 
Java程式碼  收藏程式碼
  1. /** 
  2.  * loader:類載入器 
  3.  * interfaces:目標物件實現的介面 
  4.  * h:InvocationHandler的實現類 
  5.  */  
  6. public static Object newProxyInstance(ClassLoader loader,  
  7.                       Class<?>[] interfaces,  
  8.                       InvocationHandler h)  
  9.     throws IllegalArgumentException  
  10.     {  
  11.     if (h == null) {  
  12.         throw new NullPointerException();  
  13.     }  
  14.     /* 
  15.      * Look up or generate the designated proxy class. 
  16.      */  
  17.     Class cl = getProxyClass(loader, interfaces);  
  18.     /* 
  19.      * Invoke its constructor with the designated invocation handler. 
  20.      */  
  21.     try {  
  22.             // 呼叫代理物件的構造方法(也就是$Proxy0(InvocationHandler h))  
  23.         Constructor cons = cl.getConstructor(constructorParams);  
  24.             // 生成代理類的例項並把MyInvocationHandler的例項傳給它的構造方法  
  25.         return (Object) cons.newInstance(new Object[] { h });  
  26.     } catch (NoSuchMethodException e) {  
  27.         throw new InternalError(e.toString());  
  28.     } catch (IllegalAccessException e) {  
  29.         throw new InternalError(e.toString());  
  30.     } catch (InstantiationException e) {  
  31.         throw new InternalError(e.toString());  
  32.     } catch (InvocationTargetException e) {  
  33.         throw new InternalError(e.toString());  
  34.     }  
  35.     }  


   我們再進去getProxyClass方法看一下 
Java程式碼  收藏程式碼
  1. public static Class<?> getProxyClass(ClassLoader loader,   
  2.                                          Class<?>... interfaces)  
  3.     throws IllegalArgumentException  
  4.     {  
  5.     // 如果目標類實現的介面數大於65535個則丟擲異常(我XX,誰會寫這麼NB的程式碼啊?)  
  6.     if (interfaces.length > 65535) {  
  7.         throw new IllegalArgumentException("interface limit exceeded");  
  8.     }  
  9.     // 宣告代理物件所代表的Class物件(有點拗口)  
  10.     Class proxyClass = null;  
  11.     String[] interfaceNames = new String[interfaces.length];  
  12.     Set interfaceSet = new HashSet();   // for detecting duplicates  
  13.     // 遍歷目標類所實現的介面  
  14.     for (int i = 0; i < interfaces.length; i++) {  
  15.         // 拿到目標類實現的介面的名稱  
  16.         String interfaceName = interfaces[i].getName();  
  17.         Class interfaceClass = null;  
  18.         try {  
  19.         // 載入目標類實現的介面到記憶體中  
  20.         interfaceClass = Class.forName(interfaceName, false, loader);  
  21.         } catch (ClassNotFoundException e) {  
  22.         }  
  23.         if (interfaceClass != interfaces[i]) {  
  24.         throw new IllegalArgumentException(  
  25.             interfaces[i] + " is not visible from class loader");  
  26.         }  
  27.         // 中間省略了一些無關緊要的程式碼 .......  
  28.         // 把目標類實現的介面代表的Class物件放到Set中  
  29.         interfaceSet.add(interfaceClass);  
  30.         interfaceNames[i] = interfaceName;  
  31.     }  
  32.     // 把目標類實現的介面名稱作為快取(Map)中的key  
  33.     Object key = Arrays.asList(interfaceNames);  
  34.     Map cache;  
  35.     synchronized (loaderToCache) {  
  36.         // 從快取中獲取cache  
  37.         cache = (Map) loaderToCache.get(loader);  
  38.         if (cache == null) {  
  39.         // 如果獲取不到,則新建地個HashMap例項  
  40.         cache = new HashMap();  
  41.         // 把HashMap例項和當前載入器放到快取中  
  42.         loaderToCache.put(loader, cache);  
  43.         }  
  44.     }  
  45.     synchronized (cache) {  
  46.         do {  
  47.         // 根據介面的名稱從快取中獲取物件  
  48.         Object value = cache.get(key);  
  49.         if (value instanceof Reference) {  
  50.             proxyClass = (Class) ((Reference) value).get();  
  51.         }  
  52.         if (proxyClass != null) {  
  53.             // 如果代理物件的Class例項已經存在,則直接返回  <