JDK動態代理

        簡單的說,代理模式是在目標對象和訪問對象之間增加了一層代理對象,所有的訪問對象通過代理對象來實現對目標對象的調用。

        代理對象和目標對象實現同一個接口,由代理對象來調用目標對象,對於外部來說,代理對象就可以替代目標對象被調用。通過這種方式,代理對象中可以在正常的訪問中增加額外的邏輯,比如緩存、權限控制、日誌記錄等。

        但是這種靜態代理的模式需要增加額外的代理類的實現,Java 5開始引入了動態代理機制,實現了在運行時動態地創建出代理對象,這其實是一種方法調用的攔截,AOP就是利用了這種模式。


動態代理的使用例子 

package com.dynamic.jdk;

/**
* 目標類的接口定義
* <p/>
* Created by Vincent Tse on 12/2/15.
*/
public interface MyInterface {
   void doSomething();
}
package com.dynamic.jdk;

/**
* 目標類的具體實現
* <p/>
* Created by Vincent Tse on 12/2/15.
*/
public class MyInterfaceImpl implements MyInterface {

   @Override
   public void doSomething() {
       System.out.println("here is my real operation!");
   }
}
package com.dynamic.jdk;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
* 自定義的InvocationHandler
* 封裝具體的調用過程
* <p/>
* Created by Vincent Tse on 12/2/15.
*/
public class MyInvocationHandler implements InvocationHandler {
   //target是真正執行方法的目標對象
   private Object target;

   public MyInvocationHandler(Object target) {
       super();
       this.target = target;
   }

   /**
    * 代理對象調用的方法
    * @param proxy
    * @param method
    * @param args
    * @return
    * @throws Throwable
    */
   @Override
   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
       System.out.println("before target's operation!");
       Object result = method.invoke(target, args);
       System.out.println("after target's operation");
       return result;
   }
}
package com.dynamic.jdk;

import java.lang.reflect.Proxy;

/**
* 測試類
* <p/>
* Created by Vincent on 12/2/15.
*/
public class DynamicTest {

   public static void main(String[] args) {

       //生成目標對象
       MyInterface myInterface = new MyInterfaceImpl();

       //實例化invocationHandler對象,傳入目標對象作爲target
       MyInvocationHandler invocationHandler = new MyInvocationHandler(myInterface);

       //調用Proxy的方法生成代理對象
       MyInterface proxy = (MyInterface)Proxy.newProxyInstance(myInterface.getClass().getClassLoader(),
               new Class[]{MyInterface.class}, invocationHandler);

       //調用代理對象的方法
       proxy.doSomething();
   }
}

輸出結果:

before target's operation!

here is my real operation!

after target's operation! 


使用起來很簡單,接下來看源碼分析其中的原理

從Proxy的newProxyInstance()方法開始,這個方法就是用來生成代理對象的,需要傳入類加載器、實現的接口以及一個InvocationHandler對象。 

public static Object newProxyInstance(ClassLoader loader,
                                     Class<?>[] interfaces,
                                     InvocationHandler h)
   throws IllegalArgumentException
{
   Objects.requireNonNull(h);

   final Class<?>[] intfs = interfaces.clone();
   final SecurityManager sm = System.getSecurityManager();
   if (sm != null) {
       checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
   }

   //通過傳入的類加載器和實現接口生成代理類
   Class<?> cl = getProxyClass0(loader, intfs);

   /*
    * Invoke its constructor with the designated invocation handler.
    */
   try {
       if (sm != null) {
           checkNewProxyPermission(Reflection.getCallerClass(), cl);
       }
                 //反射獲取代理類的構造函數
       final Constructor<?> cons = cl.getConstructor(constructorParams);
       final InvocationHandler ih = h;
       if (!Modifier.isPublic(cl.getModifiers())) {
           AccessController.doPrivileged(new PrivilegedAction<Void>() {
               public Void run() {
                   cons.setAccessible(true);
                   return null;
               }
           });
       }
       //反射生成實例,將invocationHandler傳入,之後調用invoke方法就靠它了
       return cons.newInstance(new Object[]{h});
   } catch (IllegalAccessException|InstantiationException e) {
       throw new InternalError(e.toString(), e);
   } catch (InvocationTargetException e) {
       Throwable t = e.getCause();
       if (t instanceof RuntimeException) {
           throw (RuntimeException) t;
       } else {
           throw new InternalError(t.toString(), t);
       }
   } catch (NoSuchMethodException e) {
       throw new InternalError(e.toString(), e);
   }
}



getProxyClass0()方法就是用來生成代理類的,首先檢查實現接口數量,大於65535就拋異常,感覺應該不會有實現那麼多接口的類吧。

private static Class<?> getProxyClass0(ClassLoader loader,
                                      Class<?>... interfaces) {
   if (interfaces.length > 65535) {
       throw new IllegalArgumentException("interface limit exceeded");
   }

      //從一個Cache中獲取代理類,如果沒有就重新生成
   return proxyClassCache.get(loader, interfaces);
}


proxyClassCache是一個靜態的WeakCache,定義在Proxy類裏。

WeakCache裏有兩個factory,一個是subKeyFactory, 是一個映射函數(key, parameter)->sub-key, 另一個是valueFactory,是一個映射函數(key, parameter)->value。WeakCache的get方法需要兩個參數,一個是key,另一個是parameter。

具體WeakCache的用法與原理這裏不再贅述,請參考源碼。

private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
   proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());

這裏傳入的KeyFactory和ProxyClassFactory是Proxy類中定義的靜態類,分別對應了WeakCache中的subKeyFactory和valueFactory,都實現了BiFunction接口,並實現了apply()方法。ProxyClassFactory的apply()方法裏完成的就是生成代理類的邏輯,最關鍵的是

byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
   proxyName, interfaces, accessFlags);

上面的方法用來生成代理類的字節碼,這個代理類裏會有接口的實現方法,在實現的方法中會調用InvocationHandler的invoke()方法,有興趣的可以將生成的字節碼寫到本地,用反編譯工具打開看一下。


那麼ProxyClassFactory的apply()方法是在什麼時候被調用的呢,回到WeakCache的get()方法

//生成subkey來獲取對應的supplier
Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
Supplier<V> supplier = valuesMap.get(subKey);
Factory factory = null;
//一直循環,直到從supplier中獲取到value爲止
//如果沒有supplier就生成factory並加入到緩存中(源碼說這是一個install的過程)
while (true) {
   if (supplier != null) {
       V value = supplier.get();
       if (value != null) {
           return value;
       }
   }
   
   if (factory == null) {
       //Factory是內部類,實現了Supplier接口,實現了get()方法
       //實際上在get()方法中調用了valueFactory(也就是ProxyClassFactory)的apply()方法
       //可以debug進去看
       factory = new Factory(key, parameter, subKey, valuesMap);
   }

   if (supplier == null) {
       supplier = valuesMap.putIfAbsent(subKey, factory);
       if (supplier == null) {
           supplier = factory;
       }
   } else {
       if (valuesMap.replace(subKey, supplier, factory)) {
           supplier = factory;
       } else {
           supplier = valuesMap.get(subKey);
       }
   }
}

好辛苦,終於生成代理類了,回到newProxyInstance方法,有了代理類的Class對象,就可以通過反射生成實例(調用的是帶InvocationHandler參數的構造函數),生成實例之後就可以用代理對象了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章