public interface Service {
void print();
}
複製代碼
public class MyService implements Service {
@Override
public void print() {
System.out.println("this is print");
}
}
複製代碼
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;
}
}
複製代碼
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();
}
複製代碼
若是不出意外的話,控制檯會打印出以下信息java
this is before!緩存
this is printbash
this is after!app
這說明咱們寫的方法,獲得了加強!ide
不知道你們有沒有想過,這行代碼函數
Proxy.newProxyInstance(Demo.class.getClassLoader(), new Class[]{Service.class}, new MyHandler(service));
複製代碼
返回的到底是個什麼東西?源碼分析
帶着疑問,咱們先使用反射打印一下類名試試。post
System.out.println(proxyInstance.getClass().getName());
複製代碼
獲得的結果爲ui
com.sun.proxy.$Proxy0
複製代碼
很顯然這個類並非咱們建立的。因此到這兒就應該想到了,動態代理實質,就是使用字節碼技術,從新生成了一個新類,來達到加強的效果。this
那麼加強的新類究竟是個怎樣的類呢?咱們來挖掘一下動態代理的源碼。
分析源碼的時候我通常都是根據方法的參數和返回值,大體推敲一下方法的功能,這樣能夠快速找到關鍵方法。因此後面的代碼都是被我精簡過的,你們能夠對比着源碼閱讀。
首先進入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});
}
複製代碼
也就是新類就算這個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動態代理的本質