在 Spring Boot 項目中,JDK 動態代理和 CGLIB 動態代理都是實現 AOP (面向切面編程) 的重要技術。 它們的主要區別在于代理對象的生成方式和適用范圍。 下面詳細介紹它們的使用場景:
1. JDK 動態代理 (JDK Dynamic Proxy)
-
原理:
- JDK 動態代理是 Java 提供的內置代理機制,它通過反射在運行時動態地生成代理類。
- 代理類會實現目標類實現的接口,并將接口中的方法調用委托給一個
InvocationHandler
對象來處理。 InvocationHandler
接口負責實現具體的增強邏輯,例如日志記錄、安全檢查、事務管理等。
-
使用場景:
- 目標類實現了接口: 如果目標類實現了接口,則 Spring AOP 默認使用 JDK 動態代理。
- 簡單 AOP 場景: 適用于簡單的 AOP 場景,例如只需要對接口方法進行增強的情況。
- 不需要代理類的構造函數: JDK 動態代理創建代理對象時,不需要調用目標類的構造函數。
-
優點:
- 簡單易用: JDK 動態代理是 Java 內置的代理機制,使用起來比較簡單。
- 不需要第三方庫: 不需要依賴第三方庫。
- 對接口友好: 代理類實現了目標類實現的接口,符合面向接口編程的思想。
-
缺點:
- 必須實現接口: 目標類必須實現接口,否則無法使用 JDK 動態代理。
- 只能代理接口方法: 只能代理接口中定義的方法,無法代理類自身定義的方法。
-
示例代碼:
// 接口 interface MyInterface {void doSomething(); }// 實現類 class MyClass implements MyInterface {@Overridepublic void doSomething() {System.out.println("MyClass is doing something...");} }// 調用處理器 class MyInvocationHandler implements InvocationHandler {private Object target; // 被代理的對象public MyInvocationHandler(Object target) {this.target = target;}@Overridepublic 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 動態代理 public class JDKDynamicProxyExample {public static void main(String[] args) {MyInterface target = new MyClass(); // 創建目標對象MyInvocationHandler handler = new MyInvocationHandler(target); // 創建調用處理器// 創建代理對象MyInterface proxy = (MyInterface) Proxy.newProxyInstance(MyInterface.class.getClassLoader(),new Class[] {MyInterface.class},handler);proxy.doSomething(); // 調用代理對象的方法,會被攔截} }
2. CGLIB 動態代理 (CGLIB Dynamic Proxy)
-
原理:
- CGLIB (Code Generation Library) 是一個強大的、高性能的代碼生成庫。
- CGLIB 動態代理通過在運行時動態地生成目標類的子類來實現代理。
- 代理類會繼承目標類,并重寫目標類的方法,在重寫的方法中實現增強邏輯。
- CGLIB 動態代理不需要目標類實現接口,可以直接代理類。
-
使用場景:
- 目標類沒有實現接口: 如果目標類沒有實現接口,則 Spring AOP 使用 CGLIB 動態代理。
- 需要代理類自身定義的方法: 需要代理類自身定義的方法,而不僅僅是接口方法。
- 需要更高的性能: 在某些情況下,CGLIB 動態代理的性能可能比 JDK 動態代理更好。
-
優點:
- 不需要實現接口: 目標類不需要實現接口,可以直接代理類。
- 可以代理類自身定義的方法: 可以代理類自身定義的方法,而不僅僅是接口方法。
- 性能較好: 在某些情況下,CGLIB 動態代理的性能可能比 JDK 動態代理更好。
-
缺點:
- 需要第三方庫: 需要依賴 CGLIB 庫。
- 實現復雜: CGLIB 動態代理的實現比較復雜,需要生成目標類的子類。
- final 方法無法代理: 無法代理被
final
修飾的方法。 - 對構造函數有要求: CGLIB 創建代理對象時,需要調用目標類的構造函數。 如果目標類沒有無參構造函數,則需要手動指定構造函數。
-
示例代碼:
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 {@Overridepublic 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 動態代理 public class CGLIBDynamicProxyExample {public static void main(String[] args) {Enhancer enhancer = new Enhancer(); // 創建 Enhancer 對象enhancer.setSuperclass(MyClass.class); // 設置超類enhancer.setCallback(new MyMethodInterceptor()); // 設置回調MyClass proxy = (MyClass) enhancer.create(); // 創建代理對象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 會根據目標類是否實現了接口來選擇使用 JDK 動態代理或 CGLIB 動態代理。 可以通過
@EnableAspectJAutoProxy
注解的proxyTargetClass
屬性來強制使用 CGLIB 動態代理。@Configuration @EnableAspectJAutoProxy(proxyTargetClass = true) // 強制使用 CGLIB 動態代理 public class AopConfig {// ... }
總結:
特性 | JDK 動態代理 | CGLIB 動態代理 |
---|---|---|
目標類要求 | 必須實現接口 | 不需要實現接口 |
代理對象生成方式 | 實現接口 | 繼承類 |
性能 | 一般 | 較好 |
易用性 | 簡單 | 復雜 |
是否需要第三方庫 | 否 | 是 (net.sf.cglib) |
適用場景 | 目標類實現了接口,簡單 AOP 場景 | 目標類沒有實現接口,需要代理類自身定義的方法,性能要求較高 |
@EnableAspectJAutoProxy | 默認值:false | proxyTargetClass = true |
在實際開發中,Spring AOP 會自動選擇合適的代理方式。 如果沒有特殊需求,可以使用默認配置。 如果需要強制使用 CGLIB 動態代理,可以設置 @EnableAspectJAutoProxy(proxyTargetClass = true)
。