代理模式(Proxy Pattern)是一種結構型設計模式,也叫做委托模式,它允許你提供一個間接訪問對象的方式。
用一句話描述代理模式就是:為其他對象提供一種代理以控制對這個對象的訪問
代理模式在Java中的Spring框架和Dubbo框架中都有廣泛的應用:
通過代理模式,可以實現(xiàn)對對象的訪問控制、附加功能增強、性能優(yōu)化等目的,提高系統(tǒng)的靈活性、可維護性和可擴展性。
代理模式涉及以下幾個角色:
實現(xiàn)代理模式步驟如下:
首先定義一個接口:
public interface Subject { void request();}
然后實現(xiàn)真實主題類:
public class RealSubject implements Subject { @Override public void request() { System.out.println("Real Subject handles the request."); }}
接著創(chuàng)建代理類:
public class Proxy implements Subject { private RealSubject realSubject; @Override public void request() { if (realSubject == null) { realSubject = new RealSubject(); } preRequest(); realSubject.request(); postRequest(); } //前置處理 private void preRequest() { System.out.println("Proxy performs pre-request actions."); } //后置處理 private void postRequest() { System.out.println("Proxy performs post-request actions."); }}
客戶端調(diào)用:
public static void main(String[] args) { Proxy proxy = new Proxy(); proxy.request(); }
輸出:
Proxy performs pre-request actions.Real Subject handles the request.Proxy performs post-request actions.
Tips:一個代理類,可以代理多個真實角色,并且真實角色之間允許有耦合關系。
在代理模式中,可以區(qū)分普通代理和強制代理:
上面提供的代碼例子就是普通代理,下面用代碼演示下強制代理:
// 抽象主題接口public interface Subject { /** * 待具體實現(xiàn)的方法 */ void request(); /** * 獲取每個具體實現(xiàn)對應的代理對象實例 * @return 返回對應的代理對象 */ Subject getProxy();}// 強制代理對象public class ForceProxy implements Subject { private Subject subject; public ForceProxy(Subject subject) { this.subject = subject; } /** * 待具體實現(xiàn)的方法 */ @Override public void request() { preRequest(); subject.request(); postRequest(); } /** * @return 返回對應的代理對象就是自己 */ @Override public Subject getProxy() { return this; } private void postRequest() { System.out.println("訪問真實主題以后的后續(xù)處理"); } private void preRequest() { System.out.println("訪問真實主題之前的預處理"); }}// 具體的實現(xiàn)對象public class RealSubject implements Subject { /** * 該具體實現(xiàn)對象的代理對象 */ private Subject proxy; @Override public Subject getProxy() { proxy = new ForceProxy(this); return proxy; } /** * 待具體實現(xiàn)的方法 */ @Override public void request() { if (isProxy()) { System.out.println("訪問真實主題方法"); } else { System.out.println("請使用指定的代理訪問"); } } private boolean isProxy() { return proxy != null; }}
客戶端調(diào)用:
public static void main(String[] args) { Subject subject = new RealSubject(); subject.request(); } Output: 請使用指定的代理訪問 public static void main(String[] args) { Subject subject = new RealSubject(); Subject proxy = new ForceProxy(subject); proxy.request(); } Output: 訪問真實主題之前的預處理 請使用指定的代理訪問 訪問真實主題以后的后續(xù)處理 public static void main(String[] args) { Subject subject = new RealSubject(); Subject proxy = subject.getProxy(); proxy.request(); } Output: 訪問真實主題之前的預處理 訪問真實主題方法 訪問真實主題以后的后續(xù)處理
通過代碼可以觀察到,強制代理模式下,不允許通過真實角色來直接訪問,只有通過真實角色來獲取代理對象,才能訪問。
高層模塊只需調(diào)用getProxy就可以訪問真實角色的所有方法,它根本就不需要產(chǎn)生一個代理出來,代理的管理已經(jīng)由真實角色自己完成。
前面講的普通代理和強制代理都屬于靜態(tài)代理,也就是說自己寫代理類的方式就是靜態(tài)代理。
靜態(tài)代理有一個缺點就是要在實現(xiàn)階段就要指定代理類以及被代理者,很不靈活。
而動態(tài)代理是一種在運行時動態(tài)生成代理類的機制,可以在不預先知道接口的情況下動態(tài)創(chuàng)建接口的實現(xiàn)類,允許在運行階段才指定代理哪一個對象,比如Spring AOP就是非常經(jīng)典的動態(tài)代理的應用
下面是兩個動態(tài)代理常用的實現(xiàn)方式:
JDK實現(xiàn)動態(tài)代理的核心機制就是java.lang.reflect.Proxy類和java.lang.reflect.InvocationHandler接口。
JDK動態(tài)代理的動態(tài)代理類需要去實現(xiàn)JDK自帶的java.lang.reflect.InvocationHandler接口,該接口中的invoke()方法能夠讓動態(tài)代理類實例在運行時調(diào)用被代理類需要對外實現(xiàn)的所有接口中的方法,也就是完成對真實主題類方法的調(diào)用。
具體實現(xiàn)步驟如下:
下面是動態(tài)代理的示例代碼,一起來感受一下:
// 1. 創(chuàng)建接口public interface Subject { void request();}// 2. 創(chuàng)建真實主題類public class RealSubject implements Subject { @Override public void request() { System.out.println("RealSubject handles the request."); }}// 3. 創(chuàng)建代理處理器類public class DynamicProxyHandler implements InvocationHandler { private Object target; DynamicProxyHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 執(zhí)行額外操作 System.out.println("Before requesting..."); // 調(diào)用真實主題對象的方法 Object result = method.invoke(target, args); // 執(zhí)行額外操作 System.out.println("After requesting..."); return result; }}public class DynamicProxyExample { public static void main(String[] args) { // 創(chuàng)建真實主題對象 Subject realSubject = new RealSubject(); // 創(chuàng)建代理處理器對象 InvocationHandler handler = new DynamicProxyHandler(realSubject); // 創(chuàng)建動態(tài)代理對象 Subject proxy = (Subject) Proxy.newProxyInstance( realSubject.getClass().getClassLoader(), realSubject.getClass().getInterfaces(), handler); // 調(diào)用代理對象的方法 proxy.request(); }}
這段代碼演示了使用 JDK 動態(tài)代理實現(xiàn)動態(tài)代理的過程:
這段代碼通過 JDK 動態(tài)代理機制實現(xiàn)了代理對象的動態(tài)創(chuàng)建和方法調(diào)用處理,實現(xiàn)了對真實主題對象的間接訪問,并在調(diào)用真實主題對象方法前后進行了額外的處理。
其動態(tài)調(diào)用過程如圖所示:
JDK的動態(tài)代理機制只能代理實現(xiàn)了接口的類,否則不能實現(xiàn)JDK的動態(tài)代理,具有一定的局限性。
CGLIB(Code Generation Library)是一個功能強大的字節(jié)碼生成庫,可以用來在運行時擴展Java類和實現(xiàn)動態(tài)代理。
相對于JDK動態(tài)代理基于接口的代理,cglib動態(tài)代理基于子類的代理,可以代理那些沒有接口的類,通俗說cglib可以在運行時動態(tài)生成字節(jié)碼。
cglib的原理是對指定的目標類生成一個子類,并覆蓋其中方法實現(xiàn)增強,因為采用的是繼承,所以不能對final修飾符的類進行代理。
下面是一個使用cglib實現(xiàn)動態(tài)代理的示例代碼,包括實現(xiàn)步驟:
使用 cglib 需要添加對應的依賴:
<!-- https://mvnrepository.com/artifact/cglib/cglib --><dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.3.0</version></dependency>
// 1. 創(chuàng)建真實主題類public class RealSubject { public void request() { System.out.println("RealSubject handles the request."); }}// 2. 創(chuàng)建代理處理器類public class DynamicProxyHandler implements MethodInterceptor { /** * 通過Enhancer 創(chuàng)建代理對象 */ private Enhancer enhancer = new Enhancer(); /** * 通過class對象獲取代理對象 * @param clazz class對象 * @return 代理對象 */ public Object getProxy(Class<?> clazz) { // 設置需要代理的類 enhancer.setSuperclass(clazz); // 設置enhancer的回調(diào) enhancer.setCallback(this); return enhancer.create(); } @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { // 執(zhí)行額外操作 System.out.println("Before requesting..."); // 調(diào)用真實主題對象的方法 Object result = proxy.invokeSuper(obj, args); // 執(zhí)行額外操作 System.out.println("After requesting..."); return result; }}public class CglibProxyExample { public static void main(String[] args) { DynamicProxyHandler proxy = new DynamicProxyHandler(); RealSubject realSubject = (RealSubject) proxy.getProxy(RealSubject.class); // 調(diào)用代理對象的方法 realSubject.request(); }}
輸出:
Before requesting...RealSubject handles the request.After requesting...
cglib動態(tài)代理相比于JDK動態(tài)代理的優(yōu)缺點如下:
優(yōu)點:
缺點:
代理模式是一種常用的設計模式,在軟件開發(fā)中有著廣泛的應用。通過引入代理對象,可以實現(xiàn)對真實對象的訪問控制、附加功能增強、性能優(yōu)化等目的。
優(yōu)點
缺點
總的來說,代理模式通過引入代理對象,實現(xiàn)了對真實對象的間接訪問和控制,為系統(tǒng)的設計提供了一種簡潔而有效的解決方案。在日常的軟件開發(fā)中,合理地運用代理模式可以為系統(tǒng)帶來更好的結構和性能表現(xiàn)。
本文鏈接:http://www.www897cc.com/showinfo-26-75377-0.html一文搞懂設計模式—代理模式
聲明:本網(wǎng)頁內(nèi)容旨在傳播知識,若有侵權等問題請及時與本網(wǎng)聯(lián)系,我們將在第一時間刪除處理。郵件:2376512515@qq.com