哈嘍呀~~筒子們,小之最近在看Spring
的源碼,正好遇到幾個Aop
的問題涉及到Java的動態代理,以前對這個東西大體能理解,可是沒有仔細的去看源碼,今天咱們來扒一扒它的真面目。java
理解爲何Jdk
動態代理,代理類必須實現接口git
理解Jdk
動態代理全過程github
看到Jdk
的動態代理類的類的內容 若是隻想看這個能夠直接翻到最後/(ㄒoㄒ)/算法
如何本身獲得一個代理類的內容spring
如何看源碼數組
變強緩存
public interface HelloService {
void sayHello();
}
/******************************************************/
public class HelloServiceImpl implements HelloService {
@Override
public void sayHello() {
System.out.println("給掘金大佬們低頭 (:");
}
}
/****************************************************/
public class HelloInvocationHandle implements InvocationHandler {
private Object target;
public HelloInvocationHandle(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("proxy before=======");
Object result = method.invoke(target, args);
System.out.println("proxy end=======");
return result;
}
}
複製代碼
上面的代碼,相信你們在學動態代理的時候,都有寫過,我就不贅述了,你們有想過他是怎麼實現的嗎?爲何調代理類的sayHello()就能夠直接使用InvocationHandler
裏的invoke()
邏輯呢?爲何代理類必需要實現一個接口這麼麻煩呢?咱們繼續往下面看。bash
咱們看上面的Main
方法,能夠看出來Proxy.newProxyInstance()
這個方法承載了代理類的全部的邏輯,全部的魔法都在這裏面網絡
//生成一個代理對象
HelloService proxy = (HelloService) Proxy.newProxyInstance(
Thread.currentThread().getContextClassLoader(),
hello.getClass().getInterfaces(),handle);
複製代碼
咱們點進去,看看裏面長啥樣數據結構
private static final Class<?>[] constructorParams =
{ InvocationHandler.class };
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) {
...
//這裏的interface數組裏面放的是 HelloService,就是代理類須要實現的那個接口
//這邊複製了一份等待處理
final Class<?>[] intfs = interfaces.clone();
...
//獲得了代理類的Class
Class<?> cl = getProxyClass0(loader, intfs);
...
//傳入構造對象拿到代理類的構造器
//從這裏咱們能夠猜出這個代理類一個有一個構造方法是傳入InvocationHandler進行初始化的
final Constructor<?> cons = cl.getConstructor(constructorParams);
//經過反射傳入以前咱們定義的那個HelloInvocationHandle,進行構造器實例化代理對象
return cons.newInstance(new Object[]{h});
複製代碼
簡單的分析一下上面的代碼,流程也很簡單
HelloService.class
類進行代理類的生成,這樣代理類就有了原始類的方法信息,例如sayHello()
InvocationHandler
這個構造對象拿到該代理類的構造方法HelloInvocationHandle
,構造器實例化了活生生的代理對象 從這裏咱們大體就能明白了Jdk
動態代理的原理:克隆一個接口,生成一個新類做爲代理類,這個類裏面有着咱們定義的HelloInvocationHandle進行代理邏輯的處理這裏就回答了咱們上面提到的一個問題: Jdk動態代理,代理類必須實現接口,是由於跟他的實現有關係,他規定了必需要傳一個接口去生成代理類
因此全部的謎團就在生成代理類的getProxyClass0(loader, intfs)
這個方法裏
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
...
// If the proxy class defined by the given loader implementing
// the given interfaces exists, this will simply return the cached copy;
// otherwise, it will create the proxy class via the ProxyClassFactory
//這裏從一個代理緩存的Cache中獲得代理,Cache裏面涉及虛引用實引用的東西不在咱們討論的範圍裏,咱們直接看這個類是怎麼生成的
return proxyClassCache.get(loader, interfaces);
}
//從上面的註釋和這裏咱們均可以看出代理類是從一個叫ProxyClassFactory裏生成的
private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
複製代碼
看上面的註釋咱們能夠看出到ProxyClassFactory
負責生成代理類
我這裏看的是jdk1.8
的源碼,用lambda
重構過,舊版本看到的能夠不同,主邏輯應該沒什麼變化
// prefix for all proxy class names
private static final String proxyClassNamePrefix = "$Proxy";
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
...
//這裏定義了代理類的名字,因此你每次看到的代理類都是$Proxy開頭的
String proxyName = proxyPkg + proxyClassNamePrefix + num;
//關鍵點在這裏,這裏生成一個代理類
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
}
複製代碼
ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags)
這個方法若是感興趣的大佬點進去能夠看到,裏面是用StringBuilder
拼出了這個代理類的字節碼(:,而後轉成了Byte
數組。
關鍵點來了! 這個代理類長啥樣怎麼看?Debug
沒有暴露出來啊親,看不了,小之看到上面的byte[] proxyClassFile
這個變量,露出了一絲壞笑,前面說過,這個變量裏存的是代理類字節碼內容啊!沒錯!輸出流伺候! 小之一頓操做,QWER!飛起
public final class $Proxy0 extends Proxy implements HelloService {
private static Method m1; //equals()方法
private static Method m3; //Bingo 咱們的sayHello方法()
private static Method m2; //toString()方法
private static Method m0; //hashCode()方法
//這裏就是咱們以前提交的InvocationHandler這個構造方法!!
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
return ((Boolean)super.h.invoke(this, m1, new Object[]{var1})).booleanValue();
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
//勇士來吧!看一看咱們的代理類的sayHello()方法長什麼樣子呀!!
public final void sayHello() 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)).intValue();
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")});
m3 = Class.forName("proxy.service.HelloService").getMethod("sayHello", new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
複製代碼
筒子們,請聚焦上面的代理類sayHello()
方法。真相大白了吧?代理類裏實現了接口裏的方法,所有調用了你的HelloInvocation
的invoke()
方法啦!
這裏說一下上面的super.h.invoke(this, m3, (Object[])null);
中super.h
是什麼東西,明眼的大佬都應該看出來了吧?繼承了Proxy類,是Proxy
裏定義的InvocationHandler
變量,也就是你的HelloInvocation
大佬們,是否是以爲Jdk
動態代理也不是很複雜?好像本身也能寫出來?哈哈,本篇文章還想提供一個Debug
代碼的思路,其實看源碼沒那麼難,但願能夠幫到你們,咱們下次再見啦~
對了我這裏安利一下個人Github
:點我點我 目前正在作的幾個東西:
Triple 本身寫的一個RPC
框架,練手的,功能不是很完善,主要目的是爲了藉助這個去理解網絡相關的知識點,如Netty
,想起來寫一點~
數據結構和算法 數據結構和算法是薄弱項,多學學練練好吹吹🐂🍺呀
Spring源碼分析 還在分析,裏面會整理一些問題,帶着問題去看源碼效率更高一點,歡迎你們有什麼關於Spring
的不懂的,能夠給我提Issue
,文章寫好了應該也會發到掘金,畢竟基佬多(逃。