java動(dòng)態(tài)代理底層探究
點(diǎn)擊上方藍(lán)色字體,選擇“標(biāo)星公眾號(hào)”
優(yōu)質(zhì)文章,第一時(shí)間送達(dá)
平常面試的時(shí)候,有人回問(wèn)你Spring,你會(huì)說(shuō)什么AOP什么的,然后他會(huì)越來(lái)越往深問(wèn),其實(shí)說(shuō)到AOP那么動(dòng)態(tài)代理是繞不開(kāi)的,那么今天我們一起來(lái)看看這個(gè)動(dòng)態(tài)代理是什么個(gè)一回事先貼一張圖:

上面便是動(dòng)態(tài)代理的結(jié)束以及它的實(shí)現(xiàn)方式,以及區(qū)別(我確實(shí)很懶....)but 上面的也挺通俗易懂,只要你不是初學(xué)者
-----------------------------------------華麗的分割線----------------------------------------
接下來(lái)當(dāng)然要繼續(xù)貼圖啦:

這張圖便是動(dòng)態(tài)代理大概的實(shí)現(xiàn)流程,我們看到.只有javaProxy是直接修改字節(jié)碼的,其他的都是基于ASM來(lái)操作字節(jié)碼ASM我也不太懂,然后那個(gè)javassist也是用來(lái)操作ASM的,是島國(guó)一位工程師寫(xiě)的,據(jù)說(shuō)dubbo就是用的這項(xiàng)技術(shù).
我們看到無(wú)論是何種方式它都是基于對(duì)class字節(jié)碼的修改來(lái)進(jìn)行代理的.

好的這只是一個(gè)大概的了解那么我們來(lái)寫(xiě)代碼感受感受:
整體需求就是一個(gè)普通的Service接口,
讓后一個(gè)普通的實(shí)現(xiàn)類,ServiceImpl
最后我們來(lái)個(gè)測(cè)試的小方法:來(lái)看看動(dòng)態(tài)代理的實(shí)現(xiàn):
public class ProjexTest {
public static void main(String[] args) {
final serviceImpl impl =new serviceImpl();
UserServce servce = (UserServce)Proxy.newProxyInstance(ProjexTest.class.getClassLoader(), new Class[]{UserServce.class}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代理執(zhí)行前");
try {
return method.invoke(impl, args);
} finally {
System.out.println("代理執(zhí)行后");
}
}
});
servce.get();
}我們還是很奇怪上面到底是什么東東?用我表達(dá)不好語(yǔ)言解釋一下:
首先,我們new 了一個(gè)newProxyInstance()然后我們?cè)贗nvocationHandler()里面的invoke方法來(lái)實(shí)現(xiàn)它的動(dòng)態(tài)代理.

還是貼圖方便一點(diǎn)這樣我們便實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的代理類

這里的我們get()方法就相當(dāng)于客戶端,掉的是userservice接口的方法,委托類就是serviceimpl 代理類就是我們傳的userservice.class,這樣通過(guò)接口屏蔽的底層的實(shí)現(xiàn)細(xì)節(jié),這樣透明化嘍.

對(duì)啊是如何做到代理目標(biāo)對(duì)象的呢?這個(gè)好說(shuō)我們通過(guò)代理類來(lái)分析一下:
@Test
public void buildProcyClass() throws IOException {
byte[] bytes = ProxyGenerator.generateProxyClass("UserService$proxy", new Class[]{
UserServce.class
});
String filename = String.format("%s/target/UserService$proxy.class", System.getProperty("user.dir"));
File file = new File(filename);
FileOutputStream outputStream = new FileOutputStream(file);
outputStream.write(bytes);
outputStream.flush();
outputStream.close();
}這樣我就把它代理的類輸出出來(lái)了:
public final class UserService$proxy extends Proxy implements UserServce {
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
public UserService$proxy(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);
}
}
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 String get() throws {
try {
return (String)super.h.invoke(this, m3, (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", Class.forName("java.lang.Object"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m3 = Class.forName("com.yqun.springboot.service.UserServce").getMethod("get");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
這個(gè)就是輸出來(lái)的類,我們可以看到代理過(guò)來(lái)的類:
return ((Boolean)super.h.invoke(this, m1, new Object[]{var1})).booleanValue(); 里面的參數(shù)就對(duì)應(yīng)著:
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 上問(wèn)中這個(gè)invoke里面要的參數(shù),
其實(shí)這樣我們可以清楚的看到,代理類回事怎么一回事
我們看看他是怎么生成的:追 類 可以通過(guò)了嗎看到它就是生成一個(gè)動(dòng)態(tài)代理對(duì)對(duì)象
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
/*獲取查詢遍歷已經(jīng)構(gòu)建好的class類也就是我們接口的代理類
* Look up or generate the designated proxy class.
*/
Class<?> cl = getProxyClass0(loader, intfs);
// If the proxy class defined by the given loader implementing //點(diǎn)進(jìn)去首先會(huì)通過(guò)從load緩存里面取
// the given interfaces exists, this will simply return the cached copy;//如果有對(duì)它從字節(jié)碼進(jìn)行構(gòu)建
// otherwise, it will create the proxy class via the ProxyClassFactory//沒(méi)有的話去創(chuàng)建一個(gè)新的
return proxyClassCache.get(loader, interfaces);
我們看到如果它沒(méi)有的話回去ProxyClassFactory類里面去實(shí)現(xiàn)那么我們就看看里面的方法,
-->ProxyClassFactory.apply 就是裝配自動(dòng)代理的過(guò)程 動(dòng)態(tài)代理所用的接口
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
//存儲(chǔ)所有動(dòng)態(tài)代理所對(duì)應(yīng)的接口
Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
for (Class<?> intf : interfaces) {
/*驗(yàn)證接口 也就是說(shuō)ClassLoader 對(duì)應(yīng)的接口是否已經(jīng)存在是否是同一個(gè)
* Verify that the class loader resolves the name of this
* interface to the same Class object.
*/
也就是說(shuō)對(duì)代理的類是不service的實(shí)現(xiàn)類
當(dāng)然里面還有許多亂七八糟的方法,但是我只看懂了這個(gè).這里他會(huì)對(duì)字節(jié)碼重組也就是修改class文件,相當(dāng)于吧代碼編譯成字節(jié)碼
在往深就是類加載器的方法了,這個(gè)便是對(duì)字節(jié)碼的重新裝載 也就是ClassLoad里面的
@Deprecated
protected final Class<?> defineClass(byte[] b, int off, int len)
throws ClassFormatError
{
return defineClass(null, b, off, len, null);
}的方法
總結(jié)兩張圖結(jié)尾:


最后一張圖特別重要,也就是寫(xiě)了這么長(zhǎng)時(shí)間的總計(jì)吧算是...
————————————————
版權(quán)聲明:本文為CSDN博主「一只想飛的豬豬」的原創(chuàng)文章,遵循CC 4.0 BY-SA版權(quán)協(xié)議,轉(zhuǎn)載請(qǐng)附上原文出處鏈接及本聲明。
原文鏈接:
https://blog.csdn.net/weixin_39715061/article/details/80394751
鋒哥最新SpringCloud分布式電商秒殺課程發(fā)布
??????
??長(zhǎng)按上方微信二維碼 2 秒
感謝點(diǎn)贊支持下哈 
