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