定義: Provide a surrogate(代理) or placeholder(占位符) for another object to control access(控制通路) or append ability(賦能) to it. 類型: 結構型模式 場景: 通路控制,賦能 思路: 為調用者和被調用者之間留有餘地,以應對變化。
在調用者和被調用者之間添加一個代理層,由代理層控制目标對象的引用,作為被調用者的替身,增加了通路的封裝性、靈活性、擴充性。
封裝性:代理封裝了被調用者的通路細節,不需要上層調用者了解細節和介入實作;
靈活性:代理層對被調用者的時機、方式可以靈活控制;
擴充性:代理層可以給被調用者賦予新的能力,或者強化已有能力。
代理模式給調用者和被調用者之間增加了一個緩沖,進而為以後的變化留有餘地,從一定程度上降低了耦合。

代理模式主要包含如下4個角色:
Subject(被調用者的抽象):定義被調用者想要/能夠提供的服務。request()方法是要提供的服務;
注:此處對于抽象類的了解既可以是一個抽象類(abstract class),也可以是一個或者多個接口(interface),根據被通路對象實際需要暴露和提供的能力而定,不需要局限于類圖。
RealSubject(被調用者的具體實作):服務的具體實作;
Client(調用者) : 服務的調用者,不局限于單個對象,可以是任何上層應用或者服務;
Proxy(通路代理):作為"代理"存在于調用者和被調用者之間,代替調用者通路目标服務;代理可以自行決定何時以及何種方式調用request()方法,同時提供額外的before()、after()等額外方法用于擴充目标服務的能力;
注:設計模式提供的是一種設計思路,是以代理模式的實作并不局限于類圖的實作,其中的角色依據看待問題的視角可大可小,即可以是一個具體的類或者對象,也可以是一個元件、一個服務、一種資源...
代理模式中的Proxy和RealSubject就好比經紀人和明星的關系。經紀人負責明星的公共事務,比如:簽約、談片酬、财務管理,危機公關等,這些事情都可以劃在Subject範疇,即經紀人的本職工作。同時經紀人本身又可以做一些自己的事情,比如:transfer the actor's property and visit his wife sometime.
代理模式還有一種簡化模式,即代理類直接繼承被調用者,而無須繼承抽象接口。根據繼承的特性,子類可以通過super關鍵字調用父類的公有方法,進而實作代理操作。類圖如下:
通過繼承實作代理的雖然方式簡單,但是很顯然違背了合成複用原則。然而我們不是為了模式而模式,原則隻是為了更好的指導軟體開發,而并非強制的規則,有時為了滿足業務需求,違背原則也無妨。簡化模式的具體實作如下,RealSubject無變化,此處僅列出代理類:
我們常用的cglib在實作動态代理時候,用的即是這種方式,将動态生成的代理類作為被調用類的子類,以獲得通路權限。
要通路的資源在遠端,通過代理封裝遠端資源的通路,屏蔽操作的複雜性。調用者調用代理對象如同調用本地對象一樣,對調用者透明。遠端代理可以作為分布式RPC的實作思路,比如:分布式服務架構Dubbo通過建立本地Service來代理遠端服務,實作透明化的遠端方法調用,即是一種遠端代理的實作。
"以小見大"、"懶加載",對于建立或者加載消耗性能較大的資源,可以先建立一個消耗較小的代理,等到真正需要調用資源的時候,再通過代理進行資源的建立和加載,實作"按需加載"。代理可以是資源的"縮略圖",也可以是資源的一部分。
實作資源通路權限控制,通過在代理層增權重限判斷,保護資源的安全性。可以作為權限控制的實作思路。
代理對被調用這的結果進行緩存,進而降低反複調用帶來的性能消耗。比如:通過spring-cache将查詢結果緩存到記憶體中,對于相同條件的調用,直接通路緩存,提高查詢性能,便是緩存代理的實作。
通過代理對象實作對被調用者的調用同步,防止沖突,保證并發安全;
通過代理實作對象的引用計數。
通過代理記錄對象通路日志;
對于分布式系統,提供統一的代理實作。使得通路分布式系統和通路單點無差别。比如:twemproxy作為redis叢集的代理,屏蔽了redis叢集底層分片部署的複雜性,在用戶端看來和直接通路redis單點無差别。
一定程度上降低了耦合;
降低調用服務的複雜性;
降低消耗,提高性能;(虛拟代理、緩存代理)
提升安全性;(保護代理)
相比于直接調用而言,增加了一層,增加請求耗時;
實作代理本身較為複雜;
1)AOP(Aspect Oriented Programming,面向切面的程式設計),通過預編譯技術或者運作時動态生成位元組碼技術實作對原有類/對象能力的增強。通常分為"靜态代理"和"動态代理"兩類,"靜态"和"動态"是根據生成AOP代理類的時機進行的劃分,對應到"編譯時"和"運作時"兩個階段。
靜态代理,就是在.java檔案編譯成.class檔案時,通過更改.class的位元組碼的方式為原有類生成AOP代理,在jvm加載類檔案後,位元組碼已經固定,不會再有改變,因而又稱為編譯時增強。代表就是AspectJ架構。
動态代理,是通過動态位元組碼生成技術,在運作時"臨時"生成AOP代理對象,代理對象對應的類檔案是在運作時動态編譯生成的,因而又稱為運作時增強。常用工具有JDK原生的動态代理,以及Cglib動态搭理。
原則
符合
違背
描述
單一職責
√
Client、RealSubject僅負責自己的業務實作,不用關注職責以外的事情;
裡氏替換原則
Proxy關聯Suject而非RealSubject,能夠代理具體子類的操作;
依賴倒轉原則
同上,Proxy本身針對Subject抽象程式設計,沒有針對具體的RealSubject實作程式設計;
接口隔離原則
Subject如果劃分合理,可以做到接口隔離,比如屏蔽不需要代理的方法,由RealSubject在子類中實作;
最小知識原則
同上,Subject如果劃分合理,僅暴露需要上層知道的服務,則符合;
開閉原則
修改具體的RealSubject,對上層不可見;如需要新增服務,可以通過實作新的RealSubject來擴充,故符合;
合成複用原則
Proxy調用RealSubject,使用IOC方式,屬于關聯關系,故符合;
裝飾模式,狀态模式;
1)代理模式和裝飾模式、狀态模式有何差別?
2)靜态代理和動态代理有何異同?
3)JDK原生動态代理與cglib動态代理有何異同?
4)為什麼被final修飾的方法不能夠被重寫?
<a href="http://blog.csdn.net/wenbingoon/article/details/8988553">Spring AOP 實作原理----AspectJ與CGLIB介紹</a>
<a href="http://blog.csdn.net/sunxianghuang/article/details/52094859">JVM即時編譯(JIT)</a>