先簡短的回顧下jdk動態代理用法
1.定義一個基礎的介面
public interface Service {
void print();
}
複製程式碼
2.簡單的實現一下介面
public class MyService implements Service {
@Override
public void print() {
System.out.println("this is print");
}
}
複製程式碼
3.實現jdk的InvocationHandler介面
public class MyHandler implements InvocationHandler {
private Service service;
public MyHandler(Service service){
this.service = service;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("this is before!");
Object result = method.invoke(service, args);
System.out.println("this is after!");
return result;
}
}
複製程式碼
4.呼叫Proxy類實現動態增強
public static void main(String[] args) {
Service service = new MyService();
Service proxyInstance = (Service) Proxy.newProxyInstance(Demo.class.getClassLoader(), new Class[]{Service.class}, new MyHandler(service));
proxyInstance.print();
}
複製程式碼
如果不出意外的話,控制檯會列印出如下資訊
this is before!
this is print
this is after!
這說明我們寫的方法,得到了增強!
JDK動態代理的原理
1.jdk動態代理的實質是什麼?
不知道大家有沒有想過,這行程式碼
Proxy.newProxyInstance(Demo.class.getClassLoader(), new Class[]{Service.class}, new MyHandler(service));
複製程式碼
返回的究竟是個什麼東西?
帶著疑問,我們先使用反射列印一下類名試試。
System.out.println(proxyInstance.getClass().getName());
複製程式碼
得到的結果為
com.sun.proxy.$Proxy0
複製程式碼
很顯然這個類並不是我們建立的。所以到這兒就應該想到了,動態代理實質,就是使用位元組碼技術,重新生成了一個新類,來達到增強的效果。
那麼增強的新類到底是個怎樣的類呢?我們來挖掘一下動態代理的原始碼。
2.jdk動態代理原始碼分析
分析原始碼的時候我一般都是根據方法的引數和返回值,大致推敲一下方法的功能,這樣可以快速找到關鍵方法。所以後面的程式碼都是被我精簡過的,大家可以對比著原始碼閱讀。
首先進入newProxyInstance方法,在去除掉業務不相干程式碼後如下:
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
Class<?> cl = getProxyClass0(loader, intfs);
final Constructor<?> cons = cl.getConstructor(constructorParams);
return cons.newInstance(new Object[]{h});
}
複製程式碼
- 第一步通過getProxyClass0方法,獲得了一個class物件cl
- 第二步獲得了cl物件的建構函式,這個建構函式的引數是一個 InvocationHandler型別
- 第三步通過我們傳入的InvocationHandler介面實現類h構造了cl物件的例項。
也就是新類就算這個cl,弄清除cl是啥,動態代理的原理我們就基本弄懂了。所以跟著這個目標,進入getProxyClass0方法。
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
return proxyClassCache.get(loader, interfaces);
}
複製程式碼
除去校驗的方法外,只剩下一行程式碼。所以只能在進去看看。在進入之前,先看看proxyClassCache是啥。
private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
public WeakCache(BiFunction<K, P, ?> subKeyFactory,
BiFunction<K, P, V> valueFactory) {
this.subKeyFactory = Objects.requireNonNull(subKeyFactory);
this.valueFactory = Objects.requireNonNull(valueFactory);
}
複製程式碼
現在在進入proxyClassCache的get方法裡,進入get方法咋一看程式碼有點多,其實上面都是快取相關處理(快取的話我建議等看完主流程再回頭看,那樣更有助於理解快取),我們先跳過,直接看關鍵程式碼
while (true) {
if (supplier != null) {
V value = supplier.get();
if (value != null) {
return value;
}
}
if (factory == null) {
factory = new Factory(key, parameter, subKey, valuesMap);
}
if (supplier == null) {
supplier = valuesMap.putIfAbsent(subKey, factory);
if (supplier == null) {
supplier = factory;
}
}
}
複製程式碼
關鍵程式碼經簡化後如上,這樣就能清晰的看出supplier就是new Factory(key, parameter, subKey, valuesMap)的例項。所以進入supplier的get方法。
public synchronized V get() { // serialize access
try {
value = Objects.requireNonNull(valueFactory.apply(key, parameter));
} finally {
if (value == null) { // remove us on failure
valuesMap.remove(subKey, this);
}
}
return value;
}
}
複製程式碼
進入get方法後,關鍵程式碼就一句valueFactory.apply(key, parameter),而這個valueFactory就是上面在建立WeakCache時設定的ProxyClassFactory。所以進入ProxyClassFactory的apply方法。
在apply方法中,終於看到了建立代理類的關鍵方法
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
複製程式碼
其中proxyClassFile就是新類的位元組碼,而defineClass0方法,就是載入這個新類。對於位元組碼如何構造在這兒我就不深究了。感興趣的可以自己研究。我更關心新類的結構。所以既然找到了新類的生成方法,我們就將他列印到檔案中瞧一瞧。
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
"ProxyService", new Class[]{Service.class}, Modifier.FINAL);
OutputStream outputStream = new FileOutputStream(new File("d:/JdkProxy.class"));
outputStream.write(proxyClassFile);
複製程式碼
在d盤下找到這個檔案,直接用idea開啟。
final class ProxyService extends Proxy implements Service {
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m0;
public ProxyService(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final void print() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final int hashCode() throws {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m3 = Class.forName("study.demo.jdk_proxy.Service").getMethod("print");
m2 = Class.forName("java.lang.Object").getMethod("toString");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
複製程式碼
不出意外就會看到如上程式碼了。其實看到這個程式碼後,jdk動態代理的本質心裡就應該有底了。所以後面就不用再說下去了吧。
下一篇,分析一下cglib動態代理的本質