Proxy代理模式是一種結(jié)構(gòu)型設(shè)計(jì)模式扒袖,主要解決的問題是:在直接訪問對象時帶來的問題
代理是一種常用的設(shè)計(jì)模式,其目的就是為其他對象提供一個代理以控制對某個對象的訪問瘪撇。代理類負(fù)責(zé)為委托類預(yù)處理消息感局,過濾消息并轉(zhuǎn)發(fā)消息扫茅,以及進(jìn)行消息被委托類執(zhí)行后的后續(xù)處理于微。
比如你要買或賣房子逗嫡,找個中介來幫你進(jìn)行買賣青自,中介如何操作你并不關(guān)心株依,而你關(guān)心的是最終的結(jié)果,房子是否買賣成功延窜,中間過程有中介來處理恋腕。這里的中介就像我們理解的代理模式一樣。我們不直接進(jìn)行買賣逆瑞,而是通過中介買賣荠藤。我們不直接訪問某個對象,而是訪問其代理對象获高。
為了保持行為的一致性哈肖,代理類和委托類通常會實(shí)現(xiàn)相同的接口,所以在訪問者看來兩者沒有絲毫的區(qū)別念秧。通過代理類這中間一層淤井,能有效控制對委托類對象的直接訪問,也可以很好地隱藏和保護(hù)委托類對象摊趾,同時也為實(shí)施不同控制策略預(yù)留了空間币狠,從而在設(shè)計(jì)上獲得了更大的靈活性。更通俗的說砾层,代理解決的問題當(dāng)兩個類需要通信時漩绵,引入第三方代理類,將兩個類的關(guān)系解耦肛炮,讓我們只了解代理類即可止吐,而且代理的出現(xiàn)還可以讓我們完成與另一個類之間的關(guān)系的統(tǒng)一管理宝踪,但是切記,代理類和委托類要實(shí)現(xiàn)相同的接口祟印,因?yàn)榇碚嬲{(diào)用的還是委托類的方法肴沫。
使用場合舉例:如果需要委托類處理某一業(yè)務(wù),那么我們就可以先在代理類中統(tǒng)一處理然后再調(diào)用具體實(shí)現(xiàn)類按照代理的創(chuàng)建時期蕴忆,代理類可以分為兩種:?
靜態(tài):由程序員創(chuàng)建代理類或特定工具自動生成源代碼再對其編譯颤芬。在程序運(yùn)行前代理類的.class文件就已經(jīng)存在了。
動態(tài):在程序運(yùn)行時運(yùn)用反射機(jī)制動態(tài)創(chuàng)建而成套鹅。
靜態(tài)代理:
靜態(tài)代理UML類圖:
?模式中包含的角色及其職責(zé)
Subject:抽象主題角色站蝠,抽象主題類可以是抽象類,也可以是接口卓鹿,是一個最普通的業(yè)務(wù)類型定義菱魔,無特殊要求。
RealSubject:具體主題角色吟孙,也叫被委托角色澜倦、被代理角色。是業(yè)務(wù)邏輯的具體執(zhí)行者杰妓。
Proxy:代理主題角色藻治,也叫委托類、代理類巷挥。它把所有抽象主題類定義的方法給具體主題角色實(shí)現(xiàn)桩卵,并且在具體主題角色處理完畢前后做預(yù)處理和善后工作。(最簡單的比如打印日志):
測試結(jié)果:
靜態(tài)代理類優(yōu)缺點(diǎn)
優(yōu)點(diǎn):
代理使客戶端不需要知道實(shí)現(xiàn)類怎么做的(不知道方法)倍宾,而客戶端只需知道代理的方法即可(解耦合)
缺點(diǎn):
1)代理類和委托類實(shí)現(xiàn)了相同的接口雏节,代理類通過委托類實(shí)現(xiàn)了相同的方法。這樣就出現(xiàn)了大量的代碼重復(fù)。如果接口增加一個方法,除了所有實(shí)現(xiàn)類需要實(shí)現(xiàn)這個方法外云茸,所有代理類也需要實(shí)現(xiàn)此方法馋辈。增加了代碼維護(hù)的復(fù)雜度。
2)代理對象只服務(wù)于一種類型的對象,如果要服務(wù)多類型的對象。勢必要為每一種對象都進(jìn)行代理,靜態(tài)代理在程序規(guī)模稍大時就無法勝任了排作。如上的代碼是只為Realsubject類的訪問提供了代理,但是如果還要為其他類如Realsubject2類提供代理的話亚情,就需要我們再次添加代理Realsubject2的代理類妄痪。
動態(tài)代理:
1.jdk動態(tài)代理(實(shí)現(xiàn)接口)
java動態(tài)代理類位于java.lang.reflect包下,一般主要涉及到以下兩個類:
Interface InvocationHandler:該接口中僅定義了一個方法Object:invoke(Object obj,Method method, Object[] args)楞件。在實(shí)際使用時衫生,第一個參數(shù)obj一般是指代理 類裳瘪,method是被代理的方法,如上例中的request()罪针,args為該方法的參數(shù)數(shù)組彭羹。 這個抽 象方法在代理類中動態(tài)實(shí)現(xiàn)。
Proxy:該類即為動態(tài)代理類泪酱,作用類似于上例中的ProxySubject派殷。
Protected Proxy(InvocationHandler h):構(gòu)造函數(shù),估計(jì)用于給內(nèi)部的h賦值墓阀。
Static Class getProxyClass (ClassLoader loader, Class[] interfaces):獲得一個 代理類毡惜,其中l(wèi)oader是類裝載器,interfaces是真實(shí)類所擁有的全部接口的數(shù)組斯撮。
Static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h):返回代理類的一個實(shí)例经伙,返回后的代理類可以當(dāng)作被代理類使用 (可使用被代理類的在Subject接口中聲明過的方法)。
運(yùn)行結(jié)果:
通過這種方式勿锅,被代理的對象(RealSubject)可以在運(yùn)行時動態(tài)改變帕膜,需要控制的接口 (Subject接口)可以在運(yùn)行時改變,控制的方式(DynamicSubject類)也可以動態(tài)改變溢十,從而實(shí) 現(xiàn)了非常靈活的動態(tài)代理關(guān)系垮刹。
從JDK 1.3以來,Java 語言通過java.lang.reflex庫提供的三個類直接支持代理:
? ? ?java.lang.reflect.Proxy,java.lang.reflect.InvocationHandler 和Method.
? ? Proxy類在運(yùn)行時動態(tài)創(chuàng)建代理對象茶宵,這也是dynamic proxy的由來危纫,其中最重要的是newProxyInstance,這個方法中宗挥,指明了將要代理的類的加載器乌庶,業(yè)務(wù)類接口,以及代理類要執(zhí)行動作的調(diào)用處理器(InvokeHandler)
public static Object newProxyInstance(ClassLoader loader, Class[] interfaces,InvocationHandler h) throws IllegalArgumentException
參數(shù)說明:
ClassLoader loader:類加載器
Class[] interfaces:得到全部的接口
InvocationHandler h:得到InvocationHandler接口的子類實(shí)例
Ps:類加載器
在Proxy類中的newProxyInstance()方法中需要一個ClassLoader類的實(shí)例契耿,ClassLoader實(shí)際上對應(yīng)的是類加載器瞒大,在Java中主要有一下三種類加載器;
Booststrap ClassLoader:此加載器采用C++編寫,一般開發(fā)中是看不到的搪桂;
Extendsion ClassLoader:用來進(jìn)行擴(kuò)展類的加載透敌,一般對應(yīng)的是jre\lib\ext目錄中的類;
AppClassLoader:(默認(rèn))加載classpath指定的類,是最常使用的是一種加載器踢械。
? ?當(dāng)系統(tǒng)有了一個代理對象之后酗电,對原方法的調(diào)用會首先被分派到一個調(diào)用處理器(Invocation Handler).InvocationHandler?
?對于JDK 的Proxy,有以下幾點(diǎn):
? ? 1)Interface:對于JDK proxy,業(yè)務(wù)類是需要一個Interface的
? ? 2)Proxy内列,Proxy 類是動態(tài)產(chǎn)生的撵术,這個類在調(diào)用Proxy.newProxyInstance(targetCls.getClassLoader, targetCls.getInterface,InvocationHander)之后,會產(chǎn)生一個Proxy類的實(shí)例话瞧。實(shí)際上這個Proxy類也是存在的嫩与,不僅僅是類的實(shí)例寝姿。這個Proxy類可以保存到硬盤上。
? ? 3) Method:對于業(yè)務(wù)委托類的每個方法划滋,現(xiàn)在Proxy類里面都不用靜態(tài)顯示出來
? ? 4) InvocationHandler: 這個類在業(yè)務(wù)委托類執(zhí)行時饵筑,會先調(diào)用invoke方法。invoke方法再執(zhí)行相應(yīng)的代理操作处坪,可以實(shí)現(xiàn)對業(yè)務(wù)方法的再包裝
2.cglib動態(tài)代理(繼承)
cglib是針對類來實(shí)現(xiàn)代理的根资,他的原理是對指定的目標(biāo)類生成一個子類,并覆蓋其中方法實(shí)現(xiàn)增強(qiáng)同窘,但因?yàn)椴捎玫氖抢^承嫂冻,所以不能對final修飾的類進(jìn)行代理。?
運(yùn)行結(jié)果:
CGLib采用了非常底層的字節(jié)碼技術(shù)塞椎,其原理是通過字節(jié)碼技術(shù)為一個類創(chuàng)建子類桨仿,并在子類中采用方法攔截的技術(shù)攔截所有父類方法的調(diào)用,順勢織入橫切邏輯案狠。JDK動態(tài)代理與CGLib動態(tài)代理均是實(shí)現(xiàn)Spring AOP的基礎(chǔ)服傍。
而spring中的AOP,是通過動態(tài)代理實(shí)現(xiàn)的骂铁。
一吹零、簡單來說:
JDK動態(tài)代理只能對實(shí)現(xiàn)了接口的類生成代理,而不能針對類
CGLIB是針對類實(shí)現(xiàn)代理拉庵,主要是對指定的類生成一個子類灿椅,覆蓋其中的方法(繼承)
二、Spring在選擇用JDK還是CGLiB的依據(jù):
?? (1)當(dāng)Bean實(shí)現(xiàn)接口時钞支,Spring就會用JDK的動態(tài)代理
?? (2)當(dāng)Bean沒有實(shí)現(xiàn)接口時茫蛹,Spring使用CGlib是實(shí)現(xiàn)
? ?(3)可以強(qiáng)制使用CGlib(在spring配置中加入<aop:aspectj-autoproxy proxy-target-class="true"/>)