代理模式
代理模式是一種結構型設計模式,它提供了一種替代訪問的方法,即通過代理對象來間接訪問目標對象。代理模式可以在不改變原始類代碼的情況下,增加額外的功能,如權限控制、日志記錄等。
靜態代理
靜態代理是指創建的或特定工具自動生成源代碼,在程序運行前代理類的.class
文件就已經存在了。每個代理類只能為一個接口服務,如果需要代理多個接口,則需要編寫多個代理類,這會增加維護成本。
示例:房子中介(靜態代理)
// 房屋租賃服務接口
interface HouseLeaseService {void leaseHouse();
}// 真實主題類 - 房東
class Landlord implements HouseLeaseService {public void leaseHouse() {System.out.println("房東: 出租房屋.");}
}// 靜態代理類 - 中介
class RealEstateAgent implements HouseLeaseService {private Landlord landlord;public RealEstateAgent(Landlord landlord) {this.landlord = landlord;}@Overridepublic void leaseHouse() {// 增加額外功能,例如廣告宣傳System.out.println("中介: 發布租房信息.");landlord.leaseHouse();// 增加額外功能,例如收取中介費System.out.println("中介: 收取中介費用.");}
}public class StaticProxyDemo {public static void main(String[] args) {Landlord landlord = new Landlord();HouseLeaseService agentService = new RealEstateAgent(landlord);agentService.leaseHouse();}
}
在這個例子中,RealEstateAgent
作為代理類,不僅實現了HouseLeaseService
接口,還包含了對Landlord
對象的操作,并在調用前后添加了額外的行為。
–
動態代理
動態代理是在程序運行時動態生成代理類的字節碼并加載到JVM中,因此不需要提前編寫代理類的代碼。Java提供了兩種主要的動態代理方式:JDK動態代理和CGLIB。
JDK動態代理
在JDK動態代理中有兩個重要的類Proxy
和InvocationHandler
類
-
Proxy
Proxy 提供用于創建動態代理類和實例的靜態方法,它還是由這些方法創建的所有動態代理類的超類。
-
InvocationHandler類
InvocationHandler 是代理實例的調用處理程序 實現的接口。
每個代理實例都具有一個關聯的調用處理程序。對代理實例調用方法時,將對方法調用進行編碼并將其指派到它的調用處理程序的invoke
方法。
代碼示例
首先,我們定義一個實現了InvocationHandler
接口的處理器類ProxyInvocationHandler
,它負責封裝中介的具體業務邏輯。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;// 房屋租賃服務接口
interface HouseLeaseService {void leaseHouse();
}// 真實主題類 - 房東
class Landlord implements HouseLeaseService {public void leaseHouse() {System.out.println("房東: 出租房屋.");}
}// 實現InvocationHandler接口的處理器類 - 中介
class ProxyInvocationHandler implements InvocationHandler {private final Object target;public ProxyInvocationHandler(Object target) {this.target = target; }@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 在調用真實方法之前可以做一些額外的工作System.out.println("中介: 發布租房信息.");// 調用目標對象的方法Object result = method.invoke(target, args);// 在調用真實方法之后也可以做一些額外的工作System.out.println("中介: 收取中介費用.");return result;}
}public class JdkProxyDemo {public static void main(String[] args) {// 創建真實房東對象Landlord landlord = new Landlord();// 創建動態代理實例HouseLeaseService proxyInstance = (HouseLeaseService) Proxy.newProxyInstance(landlord.getClass().getClassLoader(),landlord.getClass().getInterfaces(),new ProxyInvocationHandler(landlord));// 通過代理實例調用方法proxyInstance.leaseHouse();}
}
JDK動態代理與CGLIB動態代理的區別
特性 | JDK 動態代理 | CGLIB 動態代理 |
---|---|---|
實現方式 | 只能代理實現了接口的類(基于接口) | 通過生成目標類的子類進行代理(基于繼承) |
代理類 | - Proxy - Invocation Handler | - Enhancer - Method Interceptor |
核心機制 | 使用反射調用目標方法 | 通過ASM字節碼生成目標類的子類,重寫方法實現代理 |
性能 (jdk8) | 在一百萬運行次數內,性能快了30%左右;到五百萬運行次數后,性能快了將近一倍 |