靜態(tài)代理與動態(tài)代理

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類圖:


靜態(tài)代理類圖

?模式中包含的角色及其職責(zé)

Subject:抽象主題角色站蝠,抽象主題類可以是抽象類,也可以是接口卓鹿,是一個最普通的業(yè)務(wù)類型定義菱魔,無特殊要求。

RealSubject:具體主題角色吟孙,也叫被委托角色澜倦、被代理角色。是業(yè)務(wù)邏輯的具體執(zhí)行者杰妓。

Proxy:代理主題角色藻治,也叫委托類、代理類巷挥。它把所有抽象主題類定義的方法給具體主題角色實(shí)現(xiàn)桩卵,并且在具體主題角色處理完畢前后做預(yù)處理和善后工作。(最簡單的比如打印日志):

定義接口


實(shí)現(xiàn)接口的實(shí)現(xiàn)類


實(shí)現(xiàn)接口的靜態(tài)代理對象


客戶測試端

測試結(jié)果:

運(yùn)行結(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)接口)


動態(tài)代理類圖

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"/>)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市烁挟,隨后出現(xiàn)的幾起案子婴洼,更是在濱河造成了極大的恐慌,老刑警劉巖撼嗓,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件柬采,死亡現(xiàn)場離奇詭異,居然都是意外死亡且警,警方通過查閱死者的電腦和手機(jī)粉捻,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來斑芜,“玉大人肩刃,你說我怎么就攤上這事。” “怎么了树酪?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵浅碾,是天一觀的道長。 經(jīng)常有香客問我续语,道長垂谢,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任疮茄,我火速辦了婚禮滥朱,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘力试。我一直安慰自己徙邻,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布畸裳。 她就那樣靜靜地躺著缰犁,像睡著了一般。 火紅的嫁衣襯著肌膚如雪怖糊。 梳的紋絲不亂的頭發(fā)上帅容,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天,我揣著相機(jī)與錄音伍伤,去河邊找鬼并徘。 笑死,一個胖子當(dāng)著我的面吹牛扰魂,可吹牛的內(nèi)容都是我干的麦乞。 我是一名探鬼主播,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼劝评,長吁一口氣:“原來是場噩夢啊……” “哼姐直!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起付翁,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤简肴,失蹤者是張志新(化名)和其女友劉穎晃听,沒想到半個月后百侧,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡能扒,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年佣渴,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片初斑。...
    茶點(diǎn)故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡辛润,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出见秤,到底是詐尸還是另有隱情砂竖,我是刑警寧澤真椿,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站乎澄,受9級特大地震影響突硝,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜置济,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一解恰、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧浙于,春花似錦护盈、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至檀轨,卻和暖如春脏款,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背裤园。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工撤师, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人拧揽。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓剃盾,卻偏偏與公主長得像,于是被迫代替她去往敵國和親淤袜。 傳聞我的和親對象是個殘疾皇子痒谴,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評論 2 353

推薦閱讀更多精彩內(nèi)容