簡單的說,代理模式是在目標對象和訪問對象之間增加了一層代理對象,所有的訪問對象通過代理對象來實現對目標對象的調用。
代理對象和目標對象實現同一個接口,由代理對象來調用目標對象,對於外部來說,代理對象就可以替代目標對象被調用。通過這種方式,代理對象中可以在正常的訪問中增加額外的邏輯,比如緩存、權限控制、日誌記錄等。
但是這種靜態代理的模式需要增加額外的代理類的實現,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參數的構造函數),生成實例之後就可以用代理對象了。