Spring AOP 使用了两种主要的动态代理方式:
1. JDK 动态代理(基于接口)
-
原理:JDK 动态代理是基于接口的代理方式。它要求目标类必须实现至少一个接口,Spring 会使用
java.lang.reflect.Proxy类来创建一个代理对象,该代理对象实现目标接口,并通过InvocationHandler来拦截对目标对象的调用。 -
使用场景:当目标类实现了接口时,Spring 会使用 JDK 动态代理。如果目标类没有实现接口,则不能使用 JDK 动态代理。
-
优点:轻量级、性能较好,不需要生成额外的类,代理对象是基于接口的,符合面向接口编程的原则。
-
实现方式:
- Spring 会在目标类实现的接口上创建一个代理类,并将切面(增强逻辑)织入代理类的方法中。
- 通过反射机制,代理类会调用目标类的方法。
-
示例代码:
public interface MyService { void sayHello(); } public class MyServiceImpl implements MyService { public void sayHello() { System.out.println("Hello, world!"); } } @Aspect @Component public class LoggingAspect { @Before("execution(* MyService.sayHello(..))") public void logBefore() { System.out.println("Method is about to be called"); } }如果
MyService接口被实现,Spring 会使用 JDK 动态代理来创建MyServiceImpl的代理对象。
2. CGLIB 代理(基于子类继承)
-
原理:CGLIB 代理是基于继承的方式。它通过字节码生成技术,在运行时动态生成目标类的子类,并在子类中重写目标类的方法,从而在方法调用时插入增强逻辑。CGLIB 是使用字节码技术通过修改目标类的方法来实现代理的,因此它不依赖接口。
-
使用场景:当目标类没有实现接口时,Spring 会使用 CGLIB 代理。它适用于目标类没有接口或者接口不可修改的情况。
-
优点:可以代理没有接口的类,灵活性更高。它通过继承方式生成子类,能够增强类的所有方法。
-
缺点:
- 不能代理
final类和final方法,因为 CGLIB 通过继承生成子类,而final类和方法不能被继承或重写。 - 相比 JDK 动态代理,CGLIB 生成的代理对象较为重。
- 不能代理
-
实现方式:
- Spring 使用
CGLIB的Enhancer类来创建目标类的子类,重写目标方法并在方法执行前后加入切面逻辑。
- Spring 使用
-
示例代码:
public class MyService { public void sayHello() { System.out.println("Hello, world!"); } } @Aspect @Component public class LoggingAspect { @Before("execution(* MyService.sayHello(..))") public void logBefore() { System.out.println("Method is about to be called"); } }如果
MyService类没有实现接口,Spring 会使用 CGLIB 来创建代理对象。
3. 选择代理方式
Spring 在创建代理对象时会根据目标对象的特性(是否实现接口)来选择使用 JDK 动态代理或 CGLIB 代理:
- 如果目标对象实现了接口:Spring 默认使用 JDK 动态代理。
- 如果目标对象没有实现接口:Spring 会使用 CGLIB 动态代理。
4. 代理方式的切换
-
默认情况下:Spring 默认使用 JDK 动态代理,只有当目标类没有实现接口时,才会选择 CGLIB 代理。
-
手动配置:如果需要强制使用 CGLIB 代理,可以通过配置
@EnableAspectJAutoProxy(proxyTargetClass=true)来强制启用 CGLIB 代理,而不管目标类是否实现了接口。例如:@Configuration @EnableAspectJAutoProxy(proxyTargetClass=true) // 强制使用CGLIB代理 public class AppConfig { // 配置其他bean }这时,即使目标类实现了接口,Spring 也会选择使用 CGLIB 代理。
5. 总结
- JDK 动态代理:通过代理接口生成代理对象,只能代理实现了接口的目标对象。
- CGLIB 代理:通过继承目标类生成代理对象,可以代理没有实现接口的目标类,适用于没有接口或接口不可修改的情况。