Java代理模式(Proxy)實(shí)現(xiàn)方法詳解
一、什么是代理模式?
- 定義: 代理模式是一種結(jié)構(gòu)型設(shè)計(jì)模式。 它為另一個(gè)對象(目標(biāo)對象/被代理對象)提供一個(gè)代理(或占位符),以控制對這個(gè)對象的訪問。
- 核心思想: 通過引入一個(gè)代理對象,客戶端不直接訪問目標(biāo)對象,而是通過代理對象來間接訪問目標(biāo)對象。 代理對象可以控制對目標(biāo)對象的訪問,并可以在訪問前后添加額外的操作。
- 意圖: 控制對一個(gè)對象的訪問,可以延遲加載、訪問控制、增強(qiáng)功能等。
二、代理模式的結(jié)構(gòu)
代理模式通常包含以下幾個(gè)角色:
Subject (抽象主題):
- 定義了 RealSubject 和 Proxy 的共同接口。 客戶端通過 Subject 接口訪問目標(biāo)對象。
- 通常是一個(gè)接口或抽象類。
RealSubject (真實(shí)主題/目標(biāo)對象/被代理對象):
- 定義了真正的業(yè)務(wù)邏輯。
- 實(shí)現(xiàn)了 Subject 接口。
Proxy (代理):
- 持有 RealSubject 對象的引用。
- 實(shí)現(xiàn)了 Subject 接口,與 RealSubject 具有相同的方法。
- 控制對 RealSubject 對象的訪問,并可以在訪問前后添加額外的操作。
- 客戶端通過 Proxy 對象間接訪問 RealSubject 對象。
UML 類圖:
+----------------+ +----------------+ +----------------+ | <<Subject>> | | Proxy | | RealSubject | +----------------+ +----------------+ +----------------+ | +request() |------>| -realSubject |------>| +request() | +----------------+ | +request() | +----------------+ +preRequest() +postRequest()
三、代理模式的類型
根據(jù)代理的創(chuàng)建時(shí)間和功能,可以將代理模式分為以下幾種類型:
靜態(tài)代理 (Static Proxy):
- 特點(diǎn): 在編譯時(shí)就已經(jīng)確定了代理類和被代理類之間的關(guān)系。 代理類和被代理類都需要實(shí)現(xiàn)相同的接口。
- 優(yōu)點(diǎn): 實(shí)現(xiàn)簡單,易于理解。
- 缺點(diǎn):
- 代碼冗余: 如果需要代理多個(gè)類,就需要?jiǎng)?chuàng)建多個(gè)代理類,導(dǎo)致代碼冗余。
- 可維護(hù)性差: 如果接口發(fā)生變化,代理類和被代理類都需要進(jìn)行修改。
動(dòng)態(tài)代理 (Dynamic Proxy):
- 特點(diǎn): 在運(yùn)行時(shí)動(dòng)態(tài)地生成代理類,無需手動(dòng)創(chuàng)建代理類。
- 優(yōu)點(diǎn):
- 靈活性高: 可以代理任何實(shí)現(xiàn)了接口的類,無需修改原始代碼。
- 代碼復(fù)用: 可以使用同一個(gè)代理類來代理多個(gè)不同的類。
- 可維護(hù)性好: 如果接口發(fā)生變化,只需要修改代理邏輯,無需修改被代理類。
- 缺點(diǎn):
- 實(shí)現(xiàn)復(fù)雜: 動(dòng)態(tài)代理的實(shí)現(xiàn)比靜態(tài)代理復(fù)雜。
- 性能開銷: 動(dòng)態(tài)代理需要使用反射機(jī)制,性能比靜態(tài)代理略低。
- Java 中的動(dòng)態(tài)代理:
- JDK 動(dòng)態(tài)代理: Java 提供的內(nèi)置動(dòng)態(tài)代理機(jī)制,只能代理實(shí)現(xiàn)了接口的類。
- CGLIB 動(dòng)態(tài)代理: 第三方庫提供的動(dòng)態(tài)代理機(jī)制,可以代理沒有實(shí)現(xiàn)接口的類。
其他代理
- 保護(hù)代理 用于控制對敏感對象的訪問。
- 遠(yuǎn)程代理 用于訪問遠(yuǎn)程對象。
- 虛擬代理 通過代理延遲創(chuàng)建開銷大的對象
四、代理模式的實(shí)現(xiàn)方式 (Java)
靜態(tài)代理:
// 抽象主題 interface Image { void display(); } // 真實(shí)主題 class RealImage implements Image { private String filename; public RealImage(String filename) { this.filename = filename; loadImageFromDisk(); } private void loadImageFromDisk() { System.out.println("Loading image: " + filename); } @Override public void display() { System.out.println("Displaying image: " + filename); } } // 代理 class ImageProxy implements Image { private RealImage realImage; private String filename; public ImageProxy(String filename) { this.filename = filename; } @Override public void display() { if (realImage == null) { realImage = new RealImage(filename); } realImage.display(); } } // 客戶端代碼 public class StaticProxyExample { public static void main(String[] args) { Image image = new ImageProxy("test_image.jpg"); // 第一次調(diào)用 display() 方法,會(huì)加載圖片 image.display(); // 第二次調(diào)用 display() 方法,不會(huì)再次加載圖片 image.display(); } }
JDK 動(dòng)態(tài)代理:
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; // 抽象主題 interface Image { void display(); } // 真實(shí)主題 class RealImage implements Image { private String filename; public RealImage(String filename) { this.filename = filename; loadImageFromDisk(); } private void loadImageFromDisk() { System.out.println("Loading image: " + filename); } @Override public void display() { System.out.println("Displaying image: " + filename); } } // 調(diào)用處理器 class ImageInvocationHandler implements InvocationHandler { private Object target; // 被代理的對象 public ImageInvocationHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 在調(diào)用目標(biāo)方法之前執(zhí)行的操作 System.out.println("Before invoking method: " + method.getName()); // 調(diào)用目標(biāo)方法 Object result = method.invoke(target, args); // 在調(diào)用目標(biāo)方法之后執(zhí)行的操作 System.out.println("After invoking method: " + method.getName()); return result; } } // 客戶端代碼 public class JDKDynamicProxyExample { public static void main(String[] args) { // 創(chuàng)建被代理對象 Image realImage = new RealImage("test_image.jpg"); // 創(chuàng)建調(diào)用處理器 ImageInvocationHandler handler = new ImageInvocationHandler(realImage); // 創(chuàng)建代理對象 Image proxy = (Image) Proxy.newProxyInstance( Image.class.getClassLoader(), // 類加載器 new Class[] {Image.class}, // 代理類實(shí)現(xiàn)的接口列表 handler // 調(diào)用處理器 ); // 通過代理對象調(diào)用方法 proxy.display(); } }
CGLIB 動(dòng)態(tài)代理:
import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method; // 真實(shí)主題 (不需要實(shí)現(xiàn)接口) class RealImage { private String filename; public RealImage(String filename) { this.filename = filename; loadImageFromDisk(); } public RealImage(){} //需要一個(gè)無參構(gòu)造函數(shù) private void loadImageFromDisk() { System.out.println("Loading image: " + filename); } public void display() { System.out.println("Displaying image: " + filename); } } // 方法攔截器 class ImageMethodInterceptor implements MethodInterceptor { @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { // 在調(diào)用目標(biāo)方法之前執(zhí)行的操作 System.out.println("Before invoking method: " + method.getName()); // 調(diào)用目標(biāo)方法 Object result = proxy.invokeSuper(obj, args); // 在調(diào)用目標(biāo)方法之后執(zhí)行的操作 System.out.println("After invoking method: " + method.getName()); return result; } } // 客戶端代碼 public class CGLIBDynamicProxyExample { public static void main(String[] args) { // 創(chuàng)建 Enhancer 對象 Enhancer enhancer = new Enhancer(); // 設(shè)置超類 enhancer.setSuperclass(RealImage.class); // 設(shè)置回調(diào) enhancer.setCallback(new ImageMethodInterceptor()); // 創(chuàng)建代理對象 RealImage proxy = (RealImage) enhancer.create(); // 通過代理對象調(diào)用方法 proxy.display(); } }
五、代理模式的優(yōu)缺點(diǎn)
優(yōu)點(diǎn):
- 職責(zé)清晰: 將客戶端與目標(biāo)對象分離,降低了耦合度。
- 擴(kuò)展性好: 可以通過添加新的代理類來擴(kuò)展系統(tǒng)功能,而無需修改原始代碼。
- 保護(hù)目標(biāo)對象: 代理對象可以控制對目標(biāo)對象的訪問,保護(hù)目標(biāo)對象免受惡意訪問。
- 增強(qiáng)目標(biāo)對象的功能: 代理對象可以在訪問目標(biāo)對象前后添加額外的操作,例如日志記錄、安全檢查、延遲加載等。
缺點(diǎn):
- 增加系統(tǒng)復(fù)雜性: 引入代理對象會(huì)增加系統(tǒng)的復(fù)雜性。
- 可能降低性能: 代理對象需要進(jìn)行額外的處理,可能會(huì)降低程序的性能。
六、代理模式的應(yīng)用場景
- 遠(yuǎn)程代理 (Remote Proxy): 為遠(yuǎn)程對象提供一個(gè)本地代理,隱藏遠(yuǎn)程對象的具體實(shí)現(xiàn)細(xì)節(jié)。
- 虛擬代理 (Virtual Proxy): 為創(chuàng)建開銷大的對象提供一個(gè)代理,延遲對象的創(chuàng)建,直到真正需要使用時(shí)才創(chuàng)建。
- 保護(hù)代理 (Protection Proxy): 控制對敏感對象的訪問,只允許具有特定權(quán)限的客戶端訪問。
- 緩存代理 (Cache Proxy): 為訪問開銷大的對象提供一個(gè)緩存,提高訪問效率。
- 智能引用代理 (Smart Reference Proxy): 在訪問對象時(shí)執(zhí)行額外的操作,例如引用計(jì)數(shù)、對象鎖定等。
- AOP (面向切面編程): 使用動(dòng)態(tài)代理來實(shí)現(xiàn) AOP,例如 Spring AOP。
- 延遲加載 (Lazy Loading): 例如,Hibernate 中的延遲加載機(jī)制。
- 防火墻代理: 控制網(wǎng)絡(luò)訪問,保護(hù)內(nèi)部網(wǎng)絡(luò)。
七、總結(jié)
代理模式是一種非常有用的設(shè)計(jì)模式,它可以控制對對象的訪問,并可以在訪問前后添加額外的操作。 在 Java 中,可以使用靜態(tài)代理、JDK 動(dòng)態(tài)代理和 CGLIB 動(dòng)態(tài)代理來實(shí)現(xiàn)代理模式。 選擇哪種實(shí)現(xiàn)方式取決于具體的應(yīng)用場景和需求。
到此這篇關(guān)于Java代理模式(Proxy)實(shí)現(xiàn)方法的文章就介紹到這了,更多相關(guān)Java代理模式Proxy內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringCloud feign服務(wù)熔斷下的異常處理操作
這篇文章主要介紹了SpringCloud feign服務(wù)熔斷下的異常處理操作,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-06-06SpringAOP 如何通過JoinPoint獲取參數(shù)名和值
這篇文章主要介紹了SpringAOP 通過JoinPoint獲取參數(shù)名和值的操作,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-06-06解析Java的Spring框架的BeanPostProcessor發(fā)布處理器
這篇文章主要介紹了Java的Spring框架的BeanPostProcessor發(fā)布處理器,Spring是Java的SSH三大web開發(fā)框架之一,需要的朋友可以參考下2015-12-12Java實(shí)現(xiàn)Word/Excel/TXT轉(zhuǎn)PDF的方法
這篇文章主要介紹了Java實(shí)現(xiàn)Word/Excel/TXT轉(zhuǎn)PDF的方法,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-01-01Spring Security OAuth2 token權(quán)限隔離實(shí)例解析
這篇文章主要介紹了Spring Security OAuth2 token權(quán)限隔離實(shí)例解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-11-11