Java 动态代理及 RPC 框架介绍
所謂動(dòng)態(tài)代理,指的是語(yǔ)言提供的一種語(yǔ)法,能夠?qū)?duì)對(duì)象中不同方法的調(diào)用重定向到一個(gè)統(tǒng)一的處理函數(shù)中來(lái)。
python重寫(xiě)__getattr__函數(shù)能夠做到這一點(diǎn),就連世界上最好的語(yǔ)言也提供稱為魔術(shù)方法的__call。
這種語(yǔ)法除了能更好的實(shí)現(xiàn)動(dòng)態(tài)代理外,還是RPC框架實(shí)現(xiàn)原理的一部分。
1. 動(dòng)態(tài)代理是什么
動(dòng)態(tài)代理提供一種抽象,能夠?qū)?duì)象中不同方法的調(diào)用重定向到一個(gè)統(tǒng)一的處理函數(shù),做自定義的邏輯處理。
但是對(duì)于調(diào)用者,對(duì)此毫無(wú)察覺(jué),就好像調(diào)用的方法是用傳統(tǒng)方式實(shí)現(xiàn)的一般。
這種語(yǔ)法,在java中被稱為動(dòng)態(tài)代理。之所以叫做動(dòng)態(tài)代理,是因?yàn)樗鼙苊鈧鹘y(tǒng)代理模式實(shí)現(xiàn)中人工一個(gè)一個(gè)的將java函數(shù)轉(zhuǎn)發(fā)過(guò)去,
而是能夠讓代碼自動(dòng)做到這一點(diǎn),這樣代理類的代碼是和業(yè)務(wù)無(wú)關(guān)的,不會(huì)因?yàn)闃I(yè)務(wù)類的方法增多而逐漸龐大。
使代碼更易維護(hù)更易修改,實(shí)現(xiàn)自動(dòng)化搬磚。
實(shí)際上,被代理的類不一定位于本機(jī)類,動(dòng)態(tài)代理語(yǔ)法提供了一種抽象方式,被代理的類也可以位于遠(yuǎn)程主機(jī)上,這也是RPC框架實(shí)現(xiàn)原理的一部分。
理解了動(dòng)態(tài)代理的概念后不難發(fā)現(xiàn),動(dòng)態(tài)代理概念上有著這么幾個(gè)部分:
給調(diào)用者使用的代理類。在java中,我們發(fā)現(xiàn)動(dòng)態(tài)代理提供的抽象天然契合面向接口編程,因此它也有可能是接口。
一個(gè)統(tǒng)一的處理函數(shù),收集不同函數(shù)轉(zhuǎn)發(fā)過(guò)來(lái)的請(qǐng)求,可自定義處理邏輯集中處理。java中它可能會(huì)成為一個(gè)較獨(dú)立的部分,因此也可能是類。
2. java動(dòng)態(tài)代理機(jī)制
理解了概念,就不難理解java動(dòng)態(tài)代理的機(jī)制了。下面來(lái)看看java動(dòng)態(tài)代理機(jī)制如何代理一個(gè)本地對(duì)象。
2.1. 代理接口
首先看第一個(gè)部分,給調(diào)用者使用的代理類。在java動(dòng)態(tài)代理機(jī)制中,這個(gè)角色只能是接口。我們定義一個(gè)整數(shù)運(yùn)算接口:
interface?NumberOperationInterface?{int?add(int?a,?int?b); }2.2. 代理處理器
再看第二個(gè)角色,統(tǒng)一的處理函數(shù)。在java中它的確是類,通過(guò)實(shí)現(xiàn)InvocationHandler接口定義。
class?NumberOperationImpProxyHandler?implements?InvocationHandler?{private?Object?proxied;public?RealObjectProxyHandler(Object?proxied)?{this.proxied?=?proxied;}@Overridepublic?Object?invoke(Object?proxy,?Method?method,?Object[]?args)?throws?Throwable?{System.out.printf("調(diào)用函數(shù)%s\n",?method.getName());return?method.invoke(proxied,?args);}}由于我們的例子是代理本地對(duì)象,那么處理函數(shù)是需要被代理對(duì)象的信息??梢钥吹?#xff0c;我們從構(gòu)造函數(shù)中將被代理對(duì)象保存在該類中,即可從處理函數(shù)中訪問(wèn)到。
在invoke函數(shù)中,對(duì)代理對(duì)象的所有方法的調(diào)用都被轉(zhuǎn)發(fā)至該函數(shù)處理。在這里可以靈活的自定義各種你能想到的邏輯。在上面的代碼中,我們使用反射調(diào)用被代理對(duì)象的同名方法實(shí)現(xiàn)。
2.3. 被代理類
由于我們的示例是代理本地對(duì)象,因此還需要一個(gè)被代理對(duì)象的類:
class?NumberOperationImp?implements?NumerOperationInterface?{@Overridepublic?int?add(int?a,?int?b)?{return?a?+?b;} }2.4. 創(chuàng)建代理對(duì)象
好了,各個(gè)組成部分都定義完成。現(xiàn)在把它們組合起來(lái):
public?NumerOperationInterface?wrap(NumerOperationInterface?proxied)?{return?(NumerOperationInterface)?Proxy.newProxyInstance(NumerOperationInterface.class.getClassLoader(),new?Class[]{NumerOperationInterface.class},new?NumberOperationImpProxyHandler(proxied)); }由于java提供的這個(gè)寫(xiě)法實(shí)在是太啰嗦了,所以把它放入一個(gè)輔助函數(shù)中。
Proxy.newProxyInstance?方法能夠根據(jù)提供的接口和代理處理器創(chuàng)建代理對(duì)象。
java提供的寫(xiě)法太啰嗦了,可以考慮使用Guake提供的輔助函數(shù)簡(jiǎn)化下代碼。如下:
public?NumerOperationInterface?wrap(NumerOperationInterface?proxied)?{return?Reflection.newProxy(NumerOperationInterface.class,?new?NumberOperationImpProxyHandler(proxied)); }好了,現(xiàn)在調(diào)用下試試:
NumerOperationInterface?proxied?=?new?NumberOperationImp(); real?=?wrap(proxied); real.add(1,?2);2.5. 總結(jié)
動(dòng)態(tài)代理聽(tīng)起來(lái)是代理模式的動(dòng)態(tài)實(shí)現(xiàn),可是結(jié)合上面的最終效果,不覺(jué)得這個(gè)叫做動(dòng)態(tài)裝飾器更合適嗎??
3. 動(dòng)態(tài)代理的應(yīng)用
說(shuō)完了動(dòng)態(tài)代理的概念和實(shí)現(xiàn)機(jī)制,該看看使用動(dòng)態(tài)代理有哪些應(yīng)用。
3.1. 應(yīng)用一:代理模式/裝飾器模式的動(dòng)態(tài)實(shí)現(xiàn)
這個(gè)應(yīng)用場(chǎng)景前面據(jù)已經(jīng)提到過(guò)。
代理模式和裝飾器模式是編程當(dāng)中很常用的技巧,用于提升代碼的靈活性和可擴(kuò)展性。
傳統(tǒng)代理模式的實(shí)現(xiàn)方式比較暴力直接,需要將所有被代理類的所有方法都寫(xiě)一遍,并且一個(gè)個(gè)的手動(dòng)轉(zhuǎn)發(fā)過(guò)去。
在維護(hù)被代理類的同時(shí),作為java碼工還需要同時(shí)維護(hù)代理類的相關(guān)代碼,實(shí)在是累心。
通過(guò)使用動(dòng)態(tài)代理,動(dòng)態(tài)代理能夠自動(dòng)將代理類的相關(guān)方法轉(zhuǎn)發(fā)到被代理類,可以看到:
代理轉(zhuǎn)發(fā)的過(guò)程自動(dòng)化了,實(shí)現(xiàn)自動(dòng)化搬磚。
代理類的代碼邏輯和具體業(yè)務(wù)邏輯解耦,與業(yè)務(wù)無(wú)關(guān)。
3.2. 應(yīng)用二:實(shí)現(xiàn)AOP
是的,利用動(dòng)態(tài)代理也能實(shí)現(xiàn)AOP。仔細(xì)推演一下不能得出這個(gè)結(jié)論。我們知道:
動(dòng)態(tài)代理提供了一種方式,能夠?qū)⒎稚⒌姆椒ㄕ{(diào)用轉(zhuǎn)發(fā)到一個(gè)統(tǒng)一的處理函數(shù)處理。
AOP的實(shí)現(xiàn)需要能夠提供這樣一種機(jī)制,即在執(zhí)行函數(shù)前和執(zhí)行函數(shù)后都能執(zhí)行自己定義的鉤子。
那么,首先使用動(dòng)態(tài)代理讓代理類忠實(shí)的代理被代理類,然后處理函數(shù)中插入我們的自定義的鉤子。
之后讓代理類替換被代理類需要使用的場(chǎng)景,這樣,相當(dāng)于對(duì)該類的所有方法定義了一個(gè)切面。
不過(guò),使用動(dòng)態(tài)代理實(shí)現(xiàn)AOP特別麻煩,啰嗦。這僅僅作為一個(gè)探討的思路,來(lái)說(shuō)明動(dòng)態(tài)代理這一通用概念可以實(shí)現(xiàn)很多特定技術(shù)。
實(shí)際使用中當(dāng)然使用spring提供的AOP更為方便。
3.3. 應(yīng)用三:實(shí)現(xiàn)RPC
RPC即遠(yuǎn)程過(guò)程調(diào)用,在分布式的網(wǎng)站架構(gòu)中是一個(gè)非常重要的技術(shù),目前現(xiàn)在流行的SOA架構(gòu),微服務(wù)架構(gòu),它們的核心原理之一就是RPC調(diào)用。
從概念上來(lái)說(shuō),RPC的概念是非常簡(jiǎn)潔優(yōu)美的。RPC方法的調(diào)用和普通的方法并無(wú)二異,調(diào)用者不需要操心具體的實(shí)現(xiàn),這是抽象提供的威力。
實(shí)現(xiàn)上,它將函數(shù)調(diào)用方和函數(shù)的提供方分散在兩個(gè)不同的進(jìn)程上,中間使用網(wǎng)絡(luò)通信來(lái)進(jìn)行數(shù)據(jù)交互。
動(dòng)態(tài)代理就是實(shí)現(xiàn)RPC的技術(shù)之一。只要理解了動(dòng)態(tài)代理和RPC,我們很容易發(fā)現(xiàn)這樣一個(gè)事實(shí):
RPC調(diào)用其實(shí)是對(duì)遠(yuǎn)程另外一臺(tái)機(jī)器進(jìn)程上的對(duì)象的代理。
仔細(xì)思考RPC調(diào)用的數(shù)據(jù)流流向,就能梳理出這樣的思路:
調(diào)用方調(diào)用本地的RPC代理方法,將參數(shù)提供給該方法。
不同的RPC代理方法被轉(zhuǎn)發(fā)到一個(gè)統(tǒng)一的處理中心,該處理中心知道調(diào)用的是那個(gè)函數(shù),參數(shù)是什么。
該處理中心將調(diào)用的信息封裝打包,通過(guò)網(wǎng)絡(luò)發(fā)送給另外一個(gè)進(jìn)程。
另外一個(gè)進(jìn)程接受到調(diào)用進(jìn)程發(fā)送過(guò)來(lái)的數(shù)據(jù)包。
該進(jìn)程根據(jù)數(shù)據(jù)包中記錄的RPC調(diào)用信息,將調(diào)用分發(fā)給對(duì)應(yīng)的被代理對(duì)象的對(duì)應(yīng)方法去執(zhí)行。
返回的話思路類似。
顯而易見(jiàn),第二步,需要使用動(dòng)態(tài)代理將分散的函數(shù)調(diào)用轉(zhuǎn)發(fā)到一個(gè)統(tǒng)一的處理中心;第五步,將統(tǒng)一收集來(lái)的調(diào)用信息分發(fā)給具體的函數(shù)執(zhí)行,顯然使用反射做到這一點(diǎn)。
有了這個(gè)思路,通過(guò)利用動(dòng)態(tài)代理,反射,和網(wǎng)絡(luò)編程技術(shù),實(shí)現(xiàn)一個(gè)簡(jiǎn)易版的RPC框架也就不難了。
考慮到本文是介紹動(dòng)態(tài)代理的,關(guān)于RPC的細(xì)節(jié)實(shí)現(xiàn)有時(shí)間新開(kāi)一篇博文分析。
4. 最后
總得來(lái)說(shuō),通過(guò)一定的思考,個(gè)人覺(jué)得動(dòng)態(tài)代理的核心在于:將分散的對(duì)對(duì)象不同方法的調(diào)用轉(zhuǎn)發(fā)到一個(gè)同一的處理函數(shù)中來(lái)。
有了這個(gè)關(guān)鍵點(diǎn),很多其它技術(shù)的實(shí)現(xiàn)需要借助于動(dòng)態(tài)代理的這一個(gè)關(guān)鍵點(diǎn)實(shí)現(xiàn),也因此動(dòng)態(tài)代理也有著這么多的應(yīng)用。
總結(jié)
以上是生活随笔為你收集整理的Java 动态代理及 RPC 框架介绍的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 如何有效地做算法题
- 下一篇: 跟面试官聊到JVM,他99%会让你谈谈这