Java--->手寫一個(gè)動(dòng)態(tài)代理(原理篇)

在自己手動(dòng)來寫一個(gè)動(dòng)態(tài)代理前,我們先來了解一下什么靜態(tài)代理:

先定義一個(gè)Person接口和兩個(gè)實(shí)現(xiàn)類:


代理規(guī)則接口


被代理對(duì)象,實(shí)現(xiàn)Person代理規(guī)則


代理對(duì)象,在創(chuàng)建代理接口時(shí),讓其傳一個(gè)代理目標(biāo),實(shí)現(xiàn)被代理者同樣的接口,因?yàn)槲覀兪褂眠^代理者來去調(diào)用被代理者的方法,所以就得需要去實(shí)現(xiàn)與被代理者相同的接口



然后就沒了,這就是靜態(tài)代理,完全沒難度的感覺....

優(yōu)點(diǎn):可以在不對(duì)我們的XiaoFang 這個(gè)類進(jìn)行修改的前提下,對(duì)它進(jìn)行功能的拓展

缺點(diǎn):現(xiàn)在XiaoFang還只是想結(jié)婚而已,萬一它以后想買車,買房,買船,買飛機(jī)...買各種它想要的東西的時(shí)候,咋辦呢,難道每個(gè)都需要去寫嗎,因?yàn)榇韺?duì)象,需要實(shí)現(xiàn)與目標(biāo)對(duì)象同樣的接口,這樣會(huì)導(dǎo)致代理類非常多,這就很不爽了,維護(hù)起來就很蛋疼了,假如接口添加個(gè)方法,代理類跟被代理類都得去維護(hù)

動(dòng)態(tài)代理:

原理: 在底層拿到被代理對(duì)象引用,然后獲取接口,JDK從新生成一個(gè)類,同事這個(gè)類也是實(shí)現(xiàn)這個(gè)接口,把被代理對(duì)象的引用也拿到,然后編譯這個(gè)類獲取字節(jié)碼


這里我們需要改變一下代理者的內(nèi)部代碼了


現(xiàn)在代理者實(shí)現(xiàn)的接口為InvocationHandler,稍后我們?cè)賮砜纯礊槭裁匆獙?shí)現(xiàn)這個(gè)接口


使用的方式也變了

首先我們來看一下這個(gè)Person 到底是個(gè)什么對(duì)象


com.sun.proxy.$Proxy0? ?這里就會(huì)感覺很奇怪了,這特喵是個(gè)什么東西?

我們先看看這個(gè)對(duì)象里頭到底是什么飛機(jī)

獲取到生成類并且把它輸出到磁盤


反編譯后的$Proxy0,可以發(fā)現(xiàn)這個(gè)類,它也實(shí)現(xiàn)了我們的Person接口


這個(gè)地方很眼熟了吧


這個(gè)是我們關(guān)注的方法,重寫我們的getLove方法

super.h.invoke(this, m3, (Object[])null);? 是我們getLove方法中的執(zhí)行代碼,我們可以看到m3就是這個(gè)類中的變量,在靜態(tài)代碼塊中給它賦值


這個(gè)反射的就是我們Person接口中的getLove方法

我們?cè)賮砜纯催@個(gè)h 是個(gè)什么飛機(jī)

在Proxy類中,我們可以發(fā)現(xiàn)這個(gè)h,其實(shí)就是一個(gè)InvocationHandler而我們?cè)趯懘碚叩臅r(shí)候就是得實(shí)現(xiàn)這個(gè)InvocationHandler接口

是不是突然明白了點(diǎn)什么東西,用InvocationHandler的invoke方法,我們?cè)賮砜纯催@個(gè)invoke方法


第一個(gè)是一個(gè)對(duì)象,在$Proxy0? 中,是把$Proxy0 自己,跟那個(gè)m3,還有一個(gè)null給傳了進(jìn)來

Proxy.newProxyInstance(XiaoFang.class.getClassLoader(),XiaoFang.class.getInterfaces(),matchmaker);?

當(dāng)時(shí)我們是這么做來獲取到這個(gè)$Proxy0的,現(xiàn)在我們來看看這里面是搞什么飛機(jī)


我們?cè)赑roxy.newProxyInstance時(shí) 傳的matchmaker,也就是代理對(duì)象 h


看到這里我一眼就看到了這個(gè)getProxyClass0這個(gè)玩意,還有上面的注釋,Look up or generate the designated proxy class.


這里首先是做了一個(gè)判斷,判斷我們被代理類所實(shí)現(xiàn)的接口數(shù),又學(xué)到了一個(gè)玩意,65535,其實(shí)$Proxy0這個(gè)類就是在這個(gè)proxyClassCache的get方法中生成的,這個(gè)proxyClassCache是一個(gè)存放代理類緩存的地方,其實(shí)我們的$proxy0是動(dòng)態(tài)生成的類,而proxyClassCache就是保存這種生成的類的地方,所以他會(huì)先去里頭找,找到了就直接返回這個(gè)對(duì)象,找不到則通過ProxyClassFactory來創(chuàng)建這個(gè)類,有興趣大家可以自己去研究這個(gè)地方我就不搞得太詳細(xì)了(因?yàn)槲铱煜掳嗔?


再往下可以發(fā)現(xiàn),獲取了這個(gè)代理類的構(gòu)造函數(shù)對(duì)象,并且把h給賦值給了另外一個(gè)對(duì)象,還記得h是什么嗎,h就是我們?cè)趎ewProxyInstance的給這個(gè)方法傳的代理對(duì)象就是那個(gè)Matchmaker



最后就為這個(gè)$proxy0 創(chuàng)建了一個(gè)對(duì)象,并且返回;

所以我們?cè)谡{(diào)用這個(gè)getlove時(shí)候流程是 先調(diào)用了$proxy的getlove方法


然后再有我們?cè)趎ewProxyInstance時(shí)傳的matchmaker 上圖就變成了:

matchmaker.invoke($proxy0,me3,null);



最后還是通過我們?cè)趧?chuàng)建代理對(duì)象時(shí)傳的被代理對(duì)象來執(zhí)行的getlove方法

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容