SpringBoot項目中JDK動態(tài)代理和CGLIB動態(tài)代理的使用詳解
在 Spring Boot 項目中,JDK 動態(tài)代理和 CGLIB 動態(tài)代理都是實現(xiàn) AOP (面向切面編程) 的重要技術。 它們的主要區(qū)別在于代理對象的生成方式和適用范圍。
下面詳細介紹它們的使用場景:
1. JDK 動態(tài)代理 (JDK Dynamic Proxy)
原理:
- JDK 動態(tài)代理是 Java 提供的內置代理機制,它通過反射在運行時動態(tài)地生成代理類。
- 代理類會實現(xiàn)目標類實現(xiàn)的接口,并將接口中的方法調用委托給一個
InvocationHandler
對象來處理。 InvocationHandler
接口負責實現(xiàn)具體的增強邏輯,例如日志記錄、安全檢查、事務管理等。
使用場景:
- 目標類實現(xiàn)了接口: 如果目標類實現(xiàn)了接口,則 Spring AOP 默認使用 JDK 動態(tài)代理。
- 簡單 AOP 場景: 適用于簡單的 AOP 場景,例如只需要對接口方法進行增強的情況。
- 不需要代理類的構造函數(shù): JDK 動態(tài)代理創(chuàng)建代理對象時,不需要調用目標類的構造函數(shù)。
優(yōu)點:
- 簡單易用: JDK 動態(tài)代理是 Java 內置的代理機制,使用起來比較簡單。
- 不需要第三方庫: 不需要依賴第三方庫。
- 對接口友好: 代理類實現(xiàn)了目標類實現(xiàn)的接口,符合面向接口編程的思想。
缺點:
- 必須實現(xiàn)接口: 目標類必須實現(xiàn)接口,否則無法使用 JDK 動態(tài)代理。
- 只能代理接口方法: 只能代理接口中定義的方法,無法代理類自身定義的方法。
示例代碼:
// 接口 interface MyInterface { void doSomething(); } // 實現(xiàn)類 class MyClass implements MyInterface { @Override public void doSomething() { System.out.println("MyClass is doing something..."); } } // 調用處理器 class MyInvocationHandler implements InvocationHandler { private Object target; // 被代理的對象 public MyInvocationHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("Before method: " + method.getName()); // 前置增強 Object result = method.invoke(target, args); // 調用原始方法 System.out.println("After method: " + method.getName()); // 后置增強 return result; } } // 使用 JDK 動態(tài)代理 public class JDKDynamicProxyExample { public static void main(String[] args) { MyInterface target = new MyClass(); // 創(chuàng)建目標對象 MyInvocationHandler handler = new MyInvocationHandler(target); // 創(chuàng)建調用處理器 // 創(chuàng)建代理對象 MyInterface proxy = (MyInterface) Proxy.newProxyInstance( MyInterface.class.getClassLoader(), new Class[] {MyInterface.class}, handler ); proxy.doSomething(); // 調用代理對象的方法,會被攔截 } }
2. CGLIB 動態(tài)代理 (CGLIB Dynamic Proxy)
原理:
- CGLIB (Code Generation Library) 是一個強大的、高性能的代碼生成庫。
- CGLIB 動態(tài)代理通過在運行時動態(tài)地生成目標類的子類來實現(xiàn)代理。
- 代理類會繼承目標類,并重寫目標類的方法,在重寫的方法中實現(xiàn)增強邏輯。
- CGLIB 動態(tài)代理不需要目標類實現(xiàn)接口,可以直接代理類。
使用場景:
- 目標類沒有實現(xiàn)接口: 如果目標類沒有實現(xiàn)接口,則 Spring AOP 使用 CGLIB 動態(tài)代理。
- 需要代理類自身定義的方法: 需要代理類自身定義的方法,而不僅僅是接口方法。
- 需要更高的性能: 在某些情況下,CGLIB 動態(tài)代理的性能可能比 JDK 動態(tài)代理更好。
優(yōu)點:
- 不需要實現(xiàn)接口: 目標類不需要實現(xiàn)接口,可以直接代理類。
- 可以代理類自身定義的方法: 可以代理類自身定義的方法,而不僅僅是接口方法。
- 性能較好: 在某些情況下,CGLIB 動態(tài)代理的性能可能比 JDK 動態(tài)代理更好。
缺點:
- 需要第三方庫: 需要依賴 CGLIB 庫。
- 實現(xiàn)復雜: CGLIB 動態(tài)代理的實現(xiàn)比較復雜,需要生成目標類的子類。
- final 方法無法代理: 無法代理被
final
修飾的方法。 - 對構造函數(shù)有要求: CGLIB 創(chuàng)建代理對象時,需要調用目標類的構造函數(shù)。 如果目標類沒有無參構造函數(shù),則需要手動指定構造函數(shù)。
示例代碼:
import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method; // 目標類 class MyClass { public void doSomething() { System.out.println("MyClass is doing something..."); } } // 方法攔截器 class MyMethodInterceptor implements MethodInterceptor { @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("Before method: " + method.getName()); // 前置增強 Object result = proxy.invokeSuper(obj, args); // 調用原始方法 System.out.println("After method: " + method.getName()); // 后置增強 return result; } } // 使用 CGLIB 動態(tài)代理 public class CGLIBDynamicProxyExample { public static void main(String[] args) { Enhancer enhancer = new Enhancer(); // 創(chuàng)建 Enhancer 對象 enhancer.setSuperclass(MyClass.class); // 設置超類 enhancer.setCallback(new MyMethodInterceptor()); // 設置回調 MyClass proxy = (MyClass) enhancer.create(); // 創(chuàng)建代理對象 proxy.doSomething(); // 調用代理對象的方法,會被攔截 } }
3. Spring Boot 中的 AOP 配置
在 Spring Boot 項目中,可以通過以下方式配置 AOP:
添加 AOP 依賴:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
編寫切面類:
@Aspect @Component public class LoggingAspect { @Before("execution(* com.example.demo.service.*.*(..))") public void beforeAdvice(JoinPoint joinPoint) { System.out.println("Before executing method: " + joinPoint.getSignature()); } }
配置代理方式:
- 默認情況下,Spring AOP 會根據(jù)目標類是否實現(xiàn)了接口來選擇使用 JDK 動態(tài)代理或 CGLIB 動態(tài)代理。
- 可以通過
@EnableAspectJAutoProxy
注解的proxyTargetClass
屬性來強制使用 CGLIB 動態(tài)代理。
@Configuration @EnableAspectJAutoProxy(proxyTargetClass = true) // 強制使用 CGLIB 動態(tài)代理 public class AopConfig { // ... }
總結:
特性 | JDK 動態(tài)代理 | CGLIB 動態(tài)代理 |
---|---|---|
目標類要求 | 必須實現(xiàn)接口 | 不需要實現(xiàn)接口 |
代理對象生成方式 | 實現(xiàn)接口 | 繼承類 |
性能 | 一般 | 較好 |
易用性 | 簡單 | 復雜 |
是否需要第三方庫 | 否 | 是 (net.sf.cglib) |
適用場景 | 目標類實現(xiàn)了接口,簡單 AOP 場景 | 目標類沒有實現(xiàn)接口,需要代理類自身定義的方法,性能要求較高 |
@EnableAspectJAutoProxy | 默認值:false | proxyTargetClass = true |
在實際開發(fā)中,Spring AOP 會自動選擇合適的代理方式。 如果沒有特殊需求,可以使用默認配置。
如果需要強制使用 CGLIB 動態(tài)代理,可以設置
@EnableAspectJAutoProxy(proxyTargetClass = true)
總結
以上為個人經驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
java編程調用存儲過程中得到新增記錄id號的實現(xiàn)方法
這篇文章主要介紹了java編程調用存儲過程中得到新增記錄id號的實現(xiàn)方法,涉及Java數(shù)據(jù)庫操作中存儲過程的相關使用技巧,需要的朋友可以參考下2015-10-10Jenkins Pipeline為Kubernetes應用部署增加狀態(tài)檢測腳本優(yōu)化
這篇文章主要為大家介紹了Jenkins Pipeline為Kubernetes應用部署增加狀態(tài)檢測腳本優(yōu)化示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-12-12SpringBoot獲取http數(shù)據(jù)、打印HTTP參數(shù)的4種方式
Java的話本地打斷點可以調試獲取rest入?yún)?但是在生產環(huán)境可能我們獲取入?yún)ⅲ℉ttp?header/parameter)可能就沒有那么的輕松了,所以本文給大家介紹了SpringBoot獲取http數(shù)據(jù)、打印HTTP參數(shù)的4種方式,需要的朋友可以參考下2024-03-03springboot如何通過不同的策略動態(tài)調用不同的實現(xiàn)類
這篇文章主要介紹了springboot如何通過不同的策略動態(tài)調用不同的實現(xiàn)類,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-02-02SpringCloud-Alibaba-Sentinel-配置持久化策略詳解
這篇文章主要介紹了SpringCloud-Alibaba-Sentinel-配置持久化策略,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-03-03