1.?代理模式介紹
代理模式是一種結構型設計模式,它允許你提供一個代理對象來控制對另一個對象的訪問。代理對象通常在客戶端和目標對象之間起到中介作用,能夠在不改變目標對象的前提下增加額外的功能操作,比如延遲初始化、訪問控制、日志記錄等。
通俗來說,代理模式就像是你在現實生活中使用中介服務一樣。比如租房時,你可能不會直接聯系房東,而是通過房屋中介來完成租賃過程。中介(代理)會幫你處理一些細節問題,如看房、談判價格等,最終讓你順利租到房子。
代理模式有四種常見實現方式:
實現方式 | 核心機制 | 依賴要求 | 限制條件 | 性能 | 靈活性 | 代碼侵入性 |
---|---|---|---|---|---|---|
基于接口的靜態代理 | 手動創建代理類,實現相同接口 | 依賴接口 | 必須實現接口 | ★★★★☆ (高) | ★★☆☆☆ (低) | 高 |
基于繼承的靜態代理 | 代理類繼承目標類并重寫方法 | 不需要接口 | 不能代理final類/方法 | ★★★★☆ (高) | ★★☆☆☆ (低) | 高 |
基于JDK的動態代理 | Proxy +InvocationHandler 反射生成字節碼 | 必須實現接口 | JDK原生支持,要求有接口 | ★★★☆☆ (中等) | ★★★★★ (高) | 無 |
基于CGLIB的動態代理 | 繼承目標類+MethodInterceptor修改字節碼 | 不需要接口 | 不能代理final類/方法;需第三方庫 | ★★★★☆ (較高) | ★★★★★ (高) | 無 |
2.代碼演示
2.1?基于接口的靜態代理
實現原理:
- 代理類與目標類實現相同接口
- 代理類持有目標對象的引用
- 手動在接口方法中添加增強邏輯
示例場景:
//接口
public interface Subject {void request();
}//被代理的目標類
public class RealSubject implements Subject {@Overridepublic void request() {System.out.println("RealSubject: Handling request.");}
}//“基于接口”的靜態代理類
public class ProxySubject implements Subject {private RealSubject realSubject;@Overridepublic void request() {if (realSubject == null) {realSubject = new RealSubject();}System.out.println("ProxySubject: Pre-processing before handling request.");realSubject.request();System.out.println("ProxySubject: Post-processing after handling request.");}
}
使用方式:
public class Demo {public static void main(String[] args) {System.out.println("Using RealSubject directly:");Subject realSubject = new RealSubject();realSubject.request();System.out.println("\nUsing ProxySubject:");Subject proxySubject = new ProxySubject();proxySubject.request();}
}
2.2?基于繼承的靜態代理
實現原理:
- 代理類繼承目標類
- 重寫方法并添加增強邏輯
- 通過
super
調用父類方法
示例場景:
//被代理的目標類
public class RealSubject {public void request() {System.out.println("RealSubject: Handling request.");}
}//“基于繼承”的靜態代理方式
public class ProxySubjectByInheritance extends RealSubject {@Overridepublic void request() {System.out.println("ProxySubjectByInheritance: Pre-processing before handling request.");super.request();System.out.println("ProxySubjectByInheritance: Post-processing after handling request.");}
}
使用方式:
public class Demo {public static void main(String[] args) {System.out.println("Using RealSubject directly:");Subject realSubject = new RealSubject();realSubject.request();System.out.println("\nUsing ProxySubjectByInheritance:");Subject proxySubjectByInheritance = new ProxySubjectByInheritance();proxySubjectByInheritance.request();}
}
2.3?基于JDK的動態代理
實現原理:
- 底層使用Java的反射機制
- 運行時通過
java.lang.reflect.Proxy.newProxyInstance()
動態生成代理對象 - 通過
InvocationHandler
統一處理所有方法調用
示例場景
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;/*** “基于JDK”的動態代理(可以看作是“基于接口”的動態代理)* 要求: 代理的目標對象必須實現接口* 提供者: JDK,使用JDK官方的Proxy類創建代理對象* 代理類: 生成的代理類和被代理類基于相同的接口,只能使用用接口里的public方法**/
public class JdkProxySubject implements InvocationHandler {private Object target;/*** 創建代理對象的方法** @param target 被代理的原始對象; 被代理對象的類必須實現接口* @return 代理對象; 與被代理對象實現相同接口*/public Object getInstance(Object target) {this.target = target;// 創建對象的代理對象// 參數1:類加載器; 參數2:被代理對象的接口; 參數3:代理類return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);}/*** 攔截所有目標類的方法** @param proxy 目標類對象* @param method 目標類的方法* @param args 目標類的方法參數* @return 目標類方法的返回值* @throws Throwable 目標類方法拋出的異常*/@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("JdkProxySubject: Pre-processing before handling request.");Object result = method.invoke(target, args);System.out.println("JdkProxySubject: Post-processing after handling request.");return result;}
}
使用方式:
public class Demo {public static void main(String[] args) {System.out.println("Using RealSubject directly:");Subject realSubject = new RealSubject();realSubject.request();System.out.println("\nUsing JdkProxySubject:");JdkProxySubject jdkProxySubject = new JdkProxySubject();Subject realSubjectProxy = (Subject) jdkProxySubject.getInstance(new RealSubject());realSubjectProxy.request();}
}
2.4?基于CGLIB的動態代理
實現原理:
- 使用CGLIB庫來生成代理類,這個庫允許我們在運行時動態地創建目標類的子類
- 采用方法攔截(
MethodInterceptor
)機制 - 通過FastClass機制避免反射調用
示例場景:
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;/*** “基于CGLB”的動態代理(可以看作“基于繼承”的動態代理)* 提供者: 第三方 CGLib,使用CGLib的Enhancer類創建代理對象* 代理類: 生成的代理類是被代理類的子類,可以使用被代理類中的 public 和 protected 方法*/
public class CglibProxySubject implements MethodInterceptor {private Object target;/*** 使用CGLib創建動態代理對象** @param target 被代理的原始對象; 被代理對象不能用final修飾(final修飾后無法繼承了)* @return 代理對象; 是被代理對象的子類*/public Object getInstance(Object target) {this.target = target;Enhancer enhancer = new Enhancer();enhancer.setSuperclass(this.target.getClass());enhancer.setCallback(this);return enhancer.create();}/*** 攔截所有目標類的方法** @param obj 目標類的實例* @param method 目標類的方法* @param args 目標類方法的參數* @param proxy 目標類方法的代理* @return 目標類方法的返回值* @throws Throwable 目標類方法拋出的異常*/@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {System.out.println("CglibProxySubject: Pre-processing before handling request.");Object result = proxy.invokeSuper(obj, args);System.out.println("CglibProxySubject: Post-processing after handling request.");return result;}
}
使用方式:
public class Demo {public static void main(String[] args) {System.out.println("Using RealSubject directly:");Subject realSubject = new RealSubject();realSubject.request();System.out.println("\nUsing CglibProxySubject:");CglibProxySubject cglibProxySubject = new CglibProxySubject();RealSubject realSubjectCglibProxy = (RealSubject) cglibProxySubject.getInstance(new RealSubject());realSubjectCglibProxy.request();}
}
3.四種代理模式比較
以下是四種代理模式的對比分析及使用場景總結:
代理方式 | 優點 | 缺點 | 適用場景 |
---|---|---|---|
基于接口的靜態代理 | 1.結構清晰,易于理解和維護 2.性能最優(無反射開銷),類型安全 | 1.需要為每個被代理類編寫代理類,代碼冗余 2.類方法變更需同步修改代理類 | 1.接口方法數量少且穩定 2.需要極致性能的場景(如高頻調用)或者需要精確控制代理邏輯的場景 3.簡單項目或教學演示 |
基于繼承的靜態代理 | 1.可以代理沒有接口的類 | 1.同樣需要編寫大量代碼; 2.不能代理final類/方法 3.類方法變更需修改代理類 | 1.遺留系統改造(無接口的類) 2.需要代理第三方類庫(拿不到三方類庫的接口) 3.不涉及final修飾的類/方法 |
JDK動態代理 | 1.無需編寫代理類(動態生成),自動適配所有接口方法,減少代碼量; 2. 支持統一攔截邏輯 | 1.只能代理實現了接口的類; 2.反射調用性能略低 | 1.Spring AOP默認實現 2.需要代理接口多個方法的場景 3.代理邏輯復雜且需復用的系統(如日志/事務統一管理) |
CGLIB動態代理 | 1.可以代理沒有接口的類; 2.性能通常優于JDK動態代理,接近靜態代理(FastClass機制) | 1.不能代理final類或final方法; 2.需引入第三方庫(如Spring-core已內置) 3.生成字節碼可能增加內存開銷 | 1.代理沒有接口的類(Spring對無接口Bean的代理) 2.需要高性能代理的場景(如Hibernate延遲加載) 3.代理非final的第三方類庫 |