一篇學(xué)會常見的代理模式
1. 代理模式概述
2. 代理模式的結(jié)構(gòu)與實現(xiàn)
3. 代理模式的應(yīng)用實例
4. 遠(yuǎn)程代理
5. 虛擬代理
6. Java動態(tài)代理
7. 代理模式的優(yōu)缺點與適用環(huán)境
“Github:https://github.com/nateshao/design-demo/tree/main/JavaDesignPatterns/15-proxy
1. 代理模式概述
相信大家都聽過代理模式,有靜態(tài)代理,JDK動態(tài)代理,Cglib代理(Spring的內(nèi)容)。接下來,千羽和大家一起學(xué)習(xí)一下這些代理模式各有優(yōu)缺點和相應(yīng)的使用場景。
商品代購示意圖:
分析
- 代購商品:顧客 -> 代購網(wǎng)站 -> 商品
- 軟件開發(fā):客戶端 -> 代理對象 -> 真實對象
還有這種類型
定義:
代理模式:給某一個對象提供一個代理或占位符,并由代理對象來控制對原對象的訪問。
- 引入一個新的代理對象
- 代理對象在客戶端對象和目標(biāo)對象之間起到中介的作用
- 去掉客戶不能看到的內(nèi)容和服務(wù)或者增添客戶需要的額外的新服務(wù)
代理模式的結(jié)構(gòu)
代理模式包含以下3個角色:
- Subject(抽象主題角色)
- Proxy(代理主題角色)
- RealSubject(真實主題角色)
2. 代理模式的結(jié)構(gòu)與實現(xiàn)
抽象主題類典型代碼:
- public abstract class Subject {
- public abstract void request();
- }
真實主題類典型代碼:
- public class RealSubject extends Subject{
- public void request() {
- //業(yè)務(wù)方法具體實現(xiàn)代碼
- }
- }
代理類典型代碼:
- public class Proxy extends Subject {
- private RealSubject realSubject = new RealSubject(); //維持一個對真實主題對象的引用
- public void preRequest() {
- …...
- }
- public void request() {
- preRequest();
- realSubject.request(); //調(diào)用真實主題對象的方法
- postRequest();
- }
- public void postRequest() {
- ……
- }
- }
幾種常見的代理模式
- 遠(yuǎn)程代理(Remote Proxy):為一個位于不同的地址空間的對象提供一個本地的代理對象,這個不同的地址空間可以在同一臺主機中,也可以在另一臺主機中,遠(yuǎn)程代理又稱為大使(Ambassador)
- 虛擬代理(Virtual Proxy):如果需要創(chuàng)建一個資源消耗較大的對象,先創(chuàng)建一個消耗相對較小的對象來表示,真實對象只在需要時才會被真正創(chuàng)建
- 保護(hù)代理(Protect Proxy):控制對一個對象的訪問,可以給不同的用戶提供不同級別的使用權(quán)限
- 緩沖代理(Cache Proxy):為某一個目標(biāo)操作的結(jié)果提供臨時的存儲空間,以便多個客戶端可以共享這些結(jié)果
- 智能引用代理(Smart Reference Proxy):當(dāng)一個對象被引用時,提供一些額外的操作,例如將對象被調(diào)用的次數(shù)記錄下來等
3. 代理模式的應(yīng)用實例
某軟件公司承接了某信息咨詢公司的收費商務(wù)信息查詢系統(tǒng)的開發(fā)任務(wù),該系統(tǒng)的基本需求如下:
在進(jìn)行商務(wù)信息查詢之前用戶需要通過身份驗證,只有合法用戶才能夠使用該查詢系統(tǒng);
在進(jìn)行商務(wù)信息查詢時系統(tǒng)需要記錄查詢?nèi)罩荆员愀鶕?jù)查詢次數(shù)收取查詢費用。
該軟件公司開發(fā)人員已完成了商務(wù)信息查詢模塊的開發(fā)任務(wù),現(xiàn)希望能夠以一種松耦合的方式向原有系統(tǒng)增加身份驗證和日志記錄功能,客戶端代碼可以無區(qū)別地對待原始的商務(wù)信息查詢模塊和增加新功能之后的商務(wù)信息查詢模塊,而且可能在將來還要在該信息查詢模塊中增加一些新的功能。
現(xiàn)使用代理模式設(shè)計并實現(xiàn)該收費商務(wù)信息查詢系統(tǒng)。
實例分析及類圖:
商務(wù)信息查詢系統(tǒng)設(shè)計方案示意圖
商務(wù)信息查詢系統(tǒng)結(jié)構(gòu)圖
實例代碼
- AccessValidator:身份驗證類,業(yè)務(wù)類
- Logger:日志記錄類,業(yè)務(wù)類
- Searcher:抽象查詢類,充當(dāng)抽象主題角色
- RealSearcher:具體查詢類,充當(dāng)真實主題角色
- ProxySearcher:代理查詢類,充當(dāng)代理主題角色
- Client:客戶端測試類
結(jié)果分析
- 保護(hù)代理和智能引用代理
- 在代理類ProxySearcher中實現(xiàn)對真實主題類的權(quán)限控制和引用計數(shù)
4. 遠(yuǎn)程代理
動機
- 客戶端程序可以訪問在遠(yuǎn)程主機上的對象,遠(yuǎn)程主機可能具有更好的計算性能與處理速度,可以快速地響應(yīng)并處理客戶端的請求
- 可以將網(wǎng)絡(luò)的細(xì)節(jié)隱藏起來,使得客戶端不必考慮網(wǎng)絡(luò)的存在
- 客戶端完全可以認(rèn)為被代理的遠(yuǎn)程業(yè)務(wù)對象是在本地而不是在遠(yuǎn)程,而遠(yuǎn)程代理對象承擔(dān)了大部分的網(wǎng)絡(luò)通信工作,并負(fù)責(zé)對遠(yuǎn)程業(yè)務(wù)方法的調(diào)用
結(jié)構(gòu)
5. 虛擬代理
動機
對于一些占用系統(tǒng)資源較多或者加載時間較長的對象,可以給這些對象提供一個虛擬代理
在真實對象創(chuàng)建成功之前虛擬代理扮演真實對象的替身,而當(dāng)真實對象創(chuàng)建之后,虛擬代理將用戶的請求轉(zhuǎn)發(fā)給真實對象
使用一個“虛假”的代理對象來代表真實對象,通過代理對象來間接引用真實對象,可以在一定程度上提高系統(tǒng)的性能
應(yīng)用
由于對象本身的復(fù)雜性或者網(wǎng)絡(luò)等原因?qū)е乱粋€對象需要較長的加載時間,此時可以用一個加載時間相對較短的代理對象來代表真實對象(結(jié)合多線程技術(shù))
一個對象的加載十分耗費系統(tǒng)資源,讓那些占用大量內(nèi)存或處理起來非常復(fù)雜的對象推遲到使用它們的時候才創(chuàng)建,而在此之前用一個相對來說占用資源較少的代理對象來代表真實對象,再通過代理對象來引用真實對象(用時間換取空間)
6. Java動態(tài)代理
- 動態(tài)代理(Dynamic Proxy)可以讓系統(tǒng)在運行時根據(jù)實際需要來動態(tài)創(chuàng)建代理類,讓同一個代理類能夠代理多個不同的真實主題類而且可以代理不同的方法
- Java語言提供了對動態(tài)代理的支持,Java語言實現(xiàn)動態(tài)代理時需要用到位于java.lang.reflect包中的一些類
Proxy類
- public static Class< ? > getProxyClass(ClassLoader loader, Class... interfaces):該方法用于返回一個Class類型的代理類,在參數(shù)中需要提供類加載器并需要指定代理的接口數(shù)組(與真實主題類的接口列表一致)
- public static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h):該方法用于返回一個動態(tài)創(chuàng)建的代理類的實例,方法中第一個參數(shù)loader表示代理類的類加載器,第二個參數(shù)interfaces表示代理類所實現(xiàn)的接口列表(與真實主題類的接口列表一致),第三個參數(shù)h表示所指派的調(diào)用處理程序類
InvocationHandler接口
- InvocationHandler接口是代理處理程序類的實現(xiàn)接口,該接口作為代理實例的調(diào)用處理者的公共父類,每一個代理類的實例都可以提供一個相關(guān)的具體調(diào)用處理者(InvocationHandler接口的子類)
- public Object invoke(Object proxy, Method method, Object[] args):該方法用于處理對代理類實例的方法調(diào)用并返回相應(yīng)的結(jié)果,當(dāng)一個代理實例中的業(yè)務(wù)方法被調(diào)用時將自動調(diào)用該方法。invoke()方法包含三個參數(shù),其中第一個參數(shù)proxy表示代理類的實例,第二個參數(shù)method表示需要代理的方法,第三個參數(shù)args表示代理方法的參數(shù)數(shù)組
- 動態(tài)代理類需要在運行時指定所代理真實主題類的接口,客戶端在調(diào)用動態(tài)代理對象的方法時,調(diào)用請求會將請求自動轉(zhuǎn)發(fā)給InvocationHandler對象的invoke()方法,由invoke()方法來實現(xiàn)對請求的統(tǒng)一處理。
動態(tài)代理實例
“某軟件公司欲為公司OA系統(tǒng)數(shù)據(jù)訪問層DAO增加方法調(diào)用日志,記錄每一個方法被調(diào)用的時間和調(diào)用結(jié)果,現(xiàn)使用動態(tài)代理進(jìn)行設(shè)計和實現(xiàn)。
實例代碼
- AbstractUserDAO:抽象用戶DAO類,抽象主題角色
- AbstractDocumentDAO:抽象文檔DAO類,抽象主題角色
- UserDAO:用戶DAO類,具體主題角色
- DocumentDAO:文檔DAO類,具體主題角色
- DAOLogHandler:自定義請求處理程序類
- Client:客戶端測試類
7. 代理模式的優(yōu)缺點與適用環(huán)境
模式優(yōu)點
- 能夠協(xié)調(diào)調(diào)用者和被調(diào)用者,在一定程度上降低了系統(tǒng)的耦合度
- 客戶端可以針對抽象主題角色進(jìn)行編程,增加和更換代理類無須修改源代碼,符合開閉原則,系統(tǒng)具有較好的靈活性和可擴展性
模式優(yōu)點——逐個分析
- 遠(yuǎn)程代理:可以將一些消耗資源較多的對象和操作移至性能更好的計算機上,提高了系統(tǒng)的整體運行效率
- 虛擬代理:通過一個消耗資源較少的對象來代表一個消耗資源較多的對象,可以在一定程度上節(jié)省系統(tǒng)的運行開銷
- 緩沖代理:為某一個操作的結(jié)果提供臨時的緩存存儲空間,以便在后續(xù)使用中能夠共享這些結(jié)果,優(yōu)化系統(tǒng)性能,縮短執(zhí)行時間
- 保護(hù)代理:可以控制對一個對象的訪問權(quán)限,為不同用戶提供不同級別的使用權(quán)限
模式缺點
由于在客戶端和真實主題之間增加了代理對象,因此有些類型的代理模式可能會造成請求的處理速度變慢(例如保護(hù)代理)
實現(xiàn)代理模式需要額外的工作,而且有些代理模式的實現(xiàn)過程較為復(fù)雜(例如遠(yuǎn)程代理)
模式適用環(huán)境
當(dāng)客戶端對象需要訪問遠(yuǎn)程主機中的對象時可以使用遠(yuǎn)程代理
當(dāng)需要用一個消耗資源較少的對象來代表一個消耗資源較多的對象,從而降低系統(tǒng)開銷、縮短運行時間時可以使用虛擬代理
當(dāng)需要為某一個被頻繁訪問的操作結(jié)果提供一個臨時存儲空間,以供多個客戶端共享訪問這些結(jié)果時可以使用緩沖代理
當(dāng)需要控制對一個對象的訪問,為不同用戶提供不同級別的訪問權(quán)限時可以使用保護(hù)代理
當(dāng)需要為一個對象的訪問(引用)提供一些額外的操作時可以使用智能引用代理