摘要:什么是代理模式什么是代理模式為其他對(duì)象提供一種代理以控制對(duì)這個(gè)對(duì)象的訪問。另外一種方式是動(dòng)態(tài)代理。代理模式應(yīng)用場(chǎng)景代理模式應(yīng)用場(chǎng)景業(yè)務(wù)系統(tǒng)的非功能性需求開發(fā)這是最常用的一個(gè)場(chǎng)景。
Provide a surrogate or placeholder for another object to control access to it.
Proxy Pattern:為其他對(duì)象提供一種代理以控制對(duì)這個(gè)對(duì)象的訪問。
說人話:在不改變?cè)碱悾ɑ蚪斜淮眍悾┐a的情況下,通過引入代理類來給原始類附加功能,比如Spring AOP。
①、Subject
抽象主題角色,可以是抽象類,可以是接口,是一個(gè)最普通的業(yè)務(wù)類定義,無特殊要求。
②、RealSubject
真實(shí)主題角色,也叫被代理角色,是業(yè)務(wù)邏輯的具體執(zhí)行者。
③、Proxy
代理主題角色,也叫代理類,它負(fù)責(zé)對(duì)真實(shí)角色的應(yīng)用,把所有抽象主題類定義的方法限制委托給真實(shí)主題角色實(shí)現(xiàn),并在真實(shí)主題角色處理前后做一些預(yù)處理或善后工作。
通用代碼如下:
/** * 抽象主題類 */public interface Subject { void doSomething();}
/** * 真實(shí)主題角色 */public class RealSubject implements Subject{ @Override public void doSomething() { //TODO 具體執(zhí)行的事 }}
/** * 代理主題角色 */public class Proxy implements Subject{ //要代理的具體實(shí)現(xiàn)類 private Subject realSubject; public Proxy(Subject realSubject){ this.realSubject = realSubject; } @Override public void doSomething() { this.before(); realSubject.doSomething(); this.after(); } // 預(yù)處理 private void before(){ // TODO } // 善后處理 private void after(){ // TODO }}
比如用代理模式實(shí)現(xiàn)統(tǒng)計(jì)某個(gè)接口的耗時(shí)。
①、基于接口編程
抽象主題類:
public interface IUserController { // 登錄 String login(String username,String password); // 注冊(cè) String register(String username,String password);}
具體主題類:
public class UserController implements IUserController{ @Override public String login(String username, String password) { // TODO 登錄邏輯 return null; } @Override public String register(String username, String password) { // TODO 注冊(cè)邏輯 return null; }}
代理主題類:
public class UserControllerProxy implements IUserController{ private IUserController userController; public UserControllerProxy(IUserController userController){ this.userController = userController; } @Override public String login(String username, String password) { long startTime = System.currentTimeMillis(); // 登錄邏輯 userController.login("username","password"); long endTime = System.currentTimeMillis(); long responseTime = endTime - startTime; System.out.println("接口響應(yīng)時(shí)間:"+responseTime); return null; } @Override public String register(String username, String password) { long startTime = System.currentTimeMillis(); // 注冊(cè)邏輯 userController.register("username","password"); long endTime = System.currentTimeMillis(); long responseTime = endTime - startTime; System.out.println("接口響應(yīng)時(shí)間:"+responseTime); return null; }}
測(cè)試:
因?yàn)樵碱?UserController 和代理類 UserControllerProxy 實(shí)現(xiàn)相同的接口,是基于接口而非實(shí)現(xiàn)編程,將UserController類對(duì)象替換為UserControllerProxy類對(duì)象,不需要改動(dòng)太多代碼
public class StaticProxyTest { public static void main(String[] args) { IUserController userController = new UserControllerProxy(new UserController()); userController.login("username","password"); userController.register("username","password"); }}
在上面的代碼中,代理類和具體主題類需要實(shí)現(xiàn)相同的接口,假如具體主題類沒有實(shí)現(xiàn)接口,并且不是我們開發(fā)維護(hù)的(比如來自第三方接口),我們要統(tǒng)計(jì)這個(gè)第三方接口的耗時(shí),那應(yīng)該如何實(shí)現(xiàn)代理模式呢?
②、基于繼承
繼承具體主題類,然后擴(kuò)展其方法即可,直接看代碼。
public class UserControllerProxy extends UserController { @Override public String login(String username, String password) { long startTime = System.currentTimeMillis(); // 登錄邏輯 super.login("username","password"); long endTime = System.currentTimeMillis(); long responseTime = endTime - startTime; System.out.println("接口響應(yīng)時(shí)間:"+responseTime); return null; } @Override public String register(String username, String password) { long startTime = System.currentTimeMillis(); // 注冊(cè)邏輯 super.register("username","password"); long endTime = System.currentTimeMillis(); long responseTime = endTime - startTime; System.out.println("接口響應(yīng)時(shí)間:"+responseTime); return null; }}
在上面的例子中,有兩個(gè)問題:
①、我們需要在代理類中,將具體主題類中的所有的方法,都重新實(shí)現(xiàn)一遍,并且為每個(gè)方法都附加相似的代碼邏輯,如果方法很多,重復(fù)代碼也會(huì)很多。
②、如果要添加的附加功能的類有不止一個(gè),我們需要針對(duì)每個(gè)類都創(chuàng)建一個(gè)代理類。
那該如何解決上面的問題呢?答案就是動(dòng)態(tài)代理(Dynamic Proxy)。
動(dòng)態(tài)代理:不事先為每個(gè)原始類編寫代理類,而是在運(yùn)行的時(shí)候,動(dòng)態(tài)地創(chuàng)建原始類對(duì)應(yīng)的代理類,然后在系統(tǒng)中用代理類替換掉原始類。
JDK動(dòng)態(tài)代理:
public class DynamicProxyHandler implements InvocationHandler { private Object target; public DynamicProxyHandler(Object target){ this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { long startTime = System.currentTimeMillis(); Object result = method.invoke(this.target, args); long endTime = System.currentTimeMillis(); long responseTime = endTime - startTime; System.out.println("接口響應(yīng)時(shí)間:"+responseTime); return result; }}
測(cè)試:
public class DynamicProxyTest { public static void main(String[] args) { // 1、創(chuàng)建具體主題類 IUserController userController = new UserController(); // 2、創(chuàng)建 Handler DynamicProxyHandler proxyHandler = new DynamicProxyHandler(userController); // 3、動(dòng)態(tài)產(chǎn)生代理類 IUserController o = (IUserController)Proxy.newProxyInstance(userController.getClass().getClassLoader(), userController.getClass().getInterfaces(), proxyHandler); o.login("username","password"); o.register("username","password"); }}
這是 JDK 動(dòng)態(tài)代理,利用反射機(jī)制生成一個(gè)實(shí)現(xiàn)代理接口的匿名類,在調(diào)用具體方法前調(diào)用InvokeHandler來處理,代理對(duì)象是在程序運(yùn)行時(shí)產(chǎn)生的,而不是編譯期,要求是具體主題類必須實(shí)現(xiàn)接口。
另外一種方式是 Cglib 動(dòng)態(tài)代理。CGLIB(Code Generation Library)是一個(gè)基于ASM的字節(jié)碼生成庫(kù),它允許我們?cè)谶\(yùn)行時(shí)對(duì)字節(jié)碼進(jìn)行修改和動(dòng)態(tài)生成,也就是通過修改字節(jié)碼生成子類來處理。
Cglib 動(dòng)態(tài)代理:
public class UserController{ public String login(String username, String password) { // TODO 登錄邏輯 System.out.println("登錄"); return null; } public String register(String username, String password) { // TODO 注冊(cè)邏輯 System.out.println("注冊(cè)"); return null; }}
注意:真實(shí)主題類是沒有實(shí)現(xiàn)接口的。
public class CglibDynamicProxy implements MethodInterceptor { private Object target; public CglibDynamicProxy(Object target){ this.target = target; } // 給目標(biāo)創(chuàng)建代理對(duì)象 public Object newProxyInstance(){ // 1.工具類 Enhancer enhancer = new Enhancer(); // 2.設(shè)置父類 enhancer.setSuperclass(target.getClass()); // 3.設(shè)置回調(diào)函數(shù) enhancer.setCallback(this); // 4.創(chuàng)建子類(代理對(duì)象) return enhancer.create(); } @Override public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { long startTime = System.currentTimeMillis(); Object result = method.invoke(this.target, args); long endTime = System.currentTimeMillis(); long responseTime = endTime - startTime; System.out.println("接口響應(yīng)時(shí)間:"+responseTime); return result; }}
測(cè)試:
public class CglibDynamicProxyTest { public static void main(String[] args) { UserController userController = new UserController(); CglibDynamicProxy cglibDynamicProxy = new CglibDynamicProxy(userController); UserController o = (UserController)cglibDynamicProxy.newProxyInstance(); o.login("username","password"); o.register("username","password"); }}
①、職責(zé)清晰
真實(shí)的角色就是實(shí)現(xiàn)實(shí)際的業(yè)務(wù)邏輯, 不用關(guān)心其他非本職責(zé)的事務(wù), 通過后期的代理完成一件事務(wù), 附帶的結(jié)果就是編程簡(jiǎn)潔清晰。
②、高擴(kuò)展性
具體主題角色是隨時(shí)都會(huì)發(fā)生變化的, 只要它實(shí)現(xiàn)了接口, 甭管它如何變化,代理類完全都可以在不做任何修改的情況下使用。
①、業(yè)務(wù)系統(tǒng)的非功能性需求開發(fā)
這是最常用的一個(gè)場(chǎng)景。比如:監(jiān)控、統(tǒng)計(jì)、鑒權(quán)、限流、事務(wù)、冪等、日志。我們將這些附加功能與業(yè)務(wù)功能解耦,放到代理類中統(tǒng)一處理,讓程序員只需要關(guān)注業(yè)務(wù)方面的開發(fā)。
典型例子就是 SpringAOP。
②、RPC
RPC(遠(yuǎn)程代理) 框架也可以看作一種代理模式,通過遠(yuǎn)程代理,將網(wǎng)絡(luò)通信、數(shù)據(jù)編解碼等細(xì)節(jié)隱藏起來。客戶端在使用 RPC 服務(wù)的時(shí)候,就像使用本地函數(shù)一樣,無需了解跟服務(wù)器交互的細(xì)節(jié)。除此之外,RPC 服務(wù)的開發(fā)者也只需要開發(fā)業(yè)務(wù)邏輯,就像開發(fā)本地使用的函數(shù)一樣,不需要關(guān)注跟客戶端的交互細(xì)節(jié)。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/124128.html
摘要:場(chǎng)景描述病從口入這句成語(yǔ)告訴我們注意飲食健康,小六同學(xué)想吃蘋果,在吃蘋果之前需要清洗一下蘋果和洗一下手,吃完蘋果后,需要洗一下手保持個(gè)人衛(wèi)生十分鐘后。。。動(dòng)態(tài)代理小六委托管家來代理洗食物和洗手,小六屬于委托對(duì)象,管家屬于代理對(duì)象。 前言 為了更好的理解代理模式,首先根據(jù)生活中實(shí)際場(chǎng)景進(jìn)行模擬,讓我們?cè)谏钪腥ンw驗(yàn)設(shè)計(jì)思想的美妙。 場(chǎng)景描述 病從口入這句成語(yǔ)告訴我們注意飲食健康,小六同學(xué)...
摘要:我們今天也來做一個(gè)萬能遙控器設(shè)計(jì)模式適配器模式將一個(gè)類的接口轉(zhuǎn)換成客戶希望的另外一個(gè)接口。今天要介紹的仍然是創(chuàng)建型設(shè)計(jì)模式的一種建造者模式。設(shè)計(jì)模式的理論知識(shí)固然重要,但 計(jì)算機(jī)程序的思維邏輯 (54) - 剖析 Collections - 設(shè)計(jì)模式 上節(jié)我們提到,類 Collections 中大概有兩類功能,第一類是對(duì)容器接口對(duì)象進(jìn)行操作,第二類是返回一個(gè)容器接口對(duì)象,上節(jié)我們介紹了...
摘要:我們今天也來做一個(gè)萬能遙控器設(shè)計(jì)模式適配器模式將一個(gè)類的接口轉(zhuǎn)換成客戶希望的另外一個(gè)接口。今天要介紹的仍然是創(chuàng)建型設(shè)計(jì)模式的一種建造者模式。設(shè)計(jì)模式的理論知識(shí)固然重要,但 計(jì)算機(jī)程序的思維邏輯 (54) - 剖析 Collections - 設(shè)計(jì)模式 上節(jié)我們提到,類 Collections 中大概有兩類功能,第一類是對(duì)容器接口對(duì)象進(jìn)行操作,第二類是返回一個(gè)容器接口對(duì)象,上節(jié)我們介紹了...
摘要:簡(jiǎn)述從這篇文章起,我們將繼續(xù)邂逅設(shè)計(jì)模式系列篇中的第二篇代理模式。代理模式可以說很多初級(jí)中級(jí)開發(fā)者迷惑的設(shè)計(jì)模式。首先我們需要使用類圖直觀地表示出代理模式思想。所以基于代理模式很輕松就實(shí)現(xiàn)。簡(jiǎn)述: 從這篇文章起,我們將繼續(xù)Kotlin邂逅設(shè)計(jì)模式系列篇中的第二篇代理模式。代理模式可以說很多初級(jí)中級(jí)開發(fā)者迷惑的設(shè)計(jì)模式。但是它確實(shí)應(yīng)用很廣,不用多說大家非常熟悉的Retrofit框架,內(nèi)部使用了...
閱讀 483·2021-11-22 12:05
閱讀 1543·2021-11-17 09:33
閱讀 3589·2021-11-11 16:54
閱讀 2682·2021-10-14 09:49
閱讀 4059·2021-09-06 15:01
閱讀 1832·2019-08-29 17:23
閱讀 706·2019-08-29 14:09
閱讀 724·2019-08-29 12:28