在 Spring Boot 项目中,JDK 动态代理和 CGLIB 动态代理都是实现 AOP (面向切面编程) 的重要技术。 它们的主要区别在于代理对象的生成方式和适用范围。 下面详细介绍它们的使用场景:
1. JDK 动态代理 (JDK Dynamic Proxy)
-
原理:
- JDK 动态代理是 Java 提供的内置代理机制,它通过反射在运行时动态地生成代理类。
- 代理类会实现目标类实现的接口,并将接口中的方法调用委托给一个
InvocationHandler
对象来处理。 InvocationHandler
接口负责实现具体的增强逻辑,例如日志记录、安全检查、事务管理等。
-
使用场景:
- 目标类实现了接口: 如果目标类实现了接口,则 Spring AOP 默认使用 JDK 动态代理。
- 简单 AOP 场景: 适用于简单的 AOP 场景,例如只需要对接口方法进行增强的情况。
- 不需要代理类的构造函数: JDK 动态代理创建代理对象时,不需要调用目标类的构造函数。
-
优点:
- 简单易用: JDK 动态代理是 Java 内置的代理机制,使用起来比较简单。
- 不需要第三方库: 不需要依赖第三方库。
- 对接口友好: 代理类实现了目标类实现的接口,符合面向接口编程的思想。
-
缺点:
- 必须实现接口: 目标类必须实现接口,否则无法使用 JDK 动态代理。
- 只能代理接口方法: 只能代理接口中定义的方法,无法代理类自身定义的方法。
-
示例代码:
java">// 接口 interface MyInterface { void doSomething(); } // 实现类 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 动态代理 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 创建代理对象时,需要调用目标类的构造函数。 如果目标类没有无参构造函数,则需要手动指定构造函数。
-
示例代码:
java">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 动态代理 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>
-
编写切面类:
java">@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 动态代理。java">@Configuration @EnableAspectJAutoProxy(proxyTargetClass = true) // 强制使用 CGLIB 动态代理 public class AopConfig { // ... }
总结:
特性 | JDK 动态代理 | CGLIB 动态代理 |
---|---|---|
目标类要求 | 必须实现接口 | 不需要实现接口 |
代理对象生成方式 | 实现接口 | 继承类 |
性能 | 一般 | 较好 |
易用性 | 简单 | 复杂 |
是否需要第三方库 | 否 | 是 (net.sf.cglib) |
适用场景 | 目标类实现了接口,简单 AOP 场景 | 目标类没有实现接口,需要代理类自身定义的方法,性能要求较高 |
@EnableAspectJAutoProxy | 默认值:false | proxyTargetClass = true |
在实际开发中,Spring AOP 会自动选择合适的代理方式。 如果没有特殊需求,可以使用默认配置。 如果需要强制使用 CGLIB 动态代理,可以设置 @EnableAspectJAutoProxy(proxyTargetClass = true)
。