代理模式在Java中主要有四種實現方式。1. 靜態代理需手動編寫代理類,通過持有目標類引用并添加額外邏輯,適合小規模項目但代碼冗余;2. jdk動態代理基于接口,利用proxy和invocationhandler在運行時生成代理對象,靈活但僅限接口代理;3. cglib代理通過繼承目標類并重寫方法實現,可代理無接口類,適用范圍廣但無法處理final類或方法;4. spring aop根據目標類是否實現接口自動選擇jdk或cglib代理,也可強制使用cglib,使開發者無需關注底層實現。
代理模式在Java中是一種常見的設計模式,主要用于控制對象的訪問、增強功能或延遲加載。它通過一個代理類來間接操作目標對象,常用于AOP編程、遠程調用(RMI)、權限控制等場景。
Java中實現代理的方式有多種,下面從常見且實用的角度出發,分別介紹幾種主流的代理實現方式。
靜態代理:手動編寫代理類
靜態代理是最基礎也是最容易理解的一種代理方式。它的特點是需要為每一個目標類手動編寫一個對應的代理類。
立即學習“Java免費學習筆記(深入)”;
實現步驟如下:
- 定義一個公共接口
- 實現目標類
- 編寫代理類,持有目標類的引用,并在其方法前后添加額外邏輯
舉個例子:
public interface Service { void doSomething(); } public class RealService implements Service { public void doSomething() { System.out.println("Doing something..."); } } public class StaticProxy implements Service { private Service target; public StaticProxy(Service target) { this.target = target; } public void doSomething() { System.out.println("Before method call"); target.doSomething(); System.out.println("After method call"); } }
這種方式優點是結構清晰,缺點是代碼冗余,每增加一個服務都需要一個新的代理類。
JDK動態代理:基于接口的運行時代理生成
JDK動態代理是Java自帶的功能,利用java.lang.reflect.Proxy類和InvocationHandler接口實現在運行時動態創建代理對象。
關鍵點在于:
- 目標類必須實現至少一個接口
- 通過Proxy.newProxyInstance()方法生成代理實例
- 所有對代理對象的方法調用都會轉發到invoke()方法中處理
示例代碼:
Service proxy = (Service) Proxy.newProxyInstance( Service.class.getClassLoader(), new Class[]{Service.class}, new InvocationHandler() { private Object target = new RealService(); public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("JDK Proxy: before"); Object result = method.invoke(target, args); System.out.println("JDK Proxy: after"); return result; } } ); proxy.doSomething();
這種方式的優點是靈活性高,無需手動編寫代理類。但局限也很明顯:只能對接口進行代理,不能代理沒有實現接口的類。
CGLIB代理:基于繼承的字節碼增強
CGLIB是一個強大的第三方庫,它通過繼承目標類并重寫其方法的方式來生成代理類,因此可以代理沒有實現接口的類。
使用CGLIB的關鍵組件是Enhancer類和MethodInterceptor接口。
基本流程如下:
簡單示例:
Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(RealService.class); enhancer.setCallback(new MethodInterceptor() { public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("CGLIB Proxy: before"); Object result = proxy.invokeSuper(obj, args); System.out.println("CGLIB Proxy: after"); return result; } }); RealService proxy = (RealService) enhancer.create(); proxy.doSomething();
CGLIB適用于更廣泛的場景,尤其適合Spring這類框架中默認使用的代理機制。但要注意的是,不能代理final類和final方法。
Spring AOP中的代理機制:自動選擇JDK或CGLIB
在實際開發中,我們經常使用Spring AOP來實現日志記錄、事務管理等功能。Spring底層會根據目標類是否實現了接口來自動選擇使用JDK動態代理還是CGLIB代理。
Spring的判斷邏輯大致如下:
- 如果目標類實現了至少一個接口,優先使用JDK動態代理
- 否則使用CGLIB代理
當然你也可以強制Spring始終使用CGLIB代理,只需在配置中設置:
<aop:config proxy-target-class="true"/>
或者在注解驅動下啟用:
@EnableAspectJAutoProxy(proxyTargetClass = true)
這種機制讓開發者不需要關心底層代理是如何構建的,只需要專注于切面邏輯的編寫即可。
總的來說,Java中代理模式的實現方式各有適用場景。靜態代理適合小規模項目或教學用途;JDK動態代理和CGLIB代理則更適合實際開發中靈活擴展的需求;而Spring在此基礎上進一步封裝,使得代理機制對開發者透明化。
基本上就這些。