Spring AOP 的代理对象是在 Bean 初始化阶段 创建的,而不是在方法调用时。
这样做有以下原因:
依赖注入的需要
在 Spring 中,代理对象需要注入到其他 Bean 中。
例如:
@Autowired
private MyService myService; // 注入的是代理对象
如果代理对象在方法调用时才创建,那么在依赖注入阶段只能注入目标对象,这会导致切面逻辑无法生效。
性能优化
方法调用时动态创建代理对象会导致性能开销,包括生成代理类、实例化代理对象等。Spring 在 Bean 初始化阶段创建代理对象并缓存,可以避免这些开销。
生命周期管理
代理对象需要覆盖目标对象的整个生命周期,而不仅仅是在方法调用时。例如,在目标对象的生命周期回调(如 @PostConstruct
或销毁回调)中,也可能需要执行切面逻辑。
切面逻辑全生命周期覆盖
切面逻辑不仅适用于方法调用,还可能应用在依赖注入完成后的生命周期事件中。如果代理对象在调用时才创建,就无法覆盖这些场景。
代理对象的创建时机
Spring AOP 的代理对象是在 Bean 初始化阶段 完成的,具体过程如下:
实例化目标 Bean
Spring 首先通过构造函数或工厂方法创建目标对象。判断是否需要代理
Spring 的BeanPostProcessor
检查当前 Bean 是否需要代理,例如是否匹配 AOP 切面条件。如果需要代理,进入下一步。生成代理类
根据目标对象的类型,动态生成代理类:使用 JDK 动态代理 为实现了接口的目标对象生成代理类。
使用 CGLIB 为没有实现接口的目标对象生成子类代理类。
实例化代理对象
基于代理类创建一个代理对象,并将其注册到容器中,替代目标对象。
AOP 代理类和代理对象的关系
代理类
动态生成的类,用于描述如何拦截目标对象的方法调用。例如,当目标类实现接口时,Spring 通过 JDK 动态代理生成类似以下代码的代理类:
public class MyServiceProxy implements MyService {
private MyAspect aspect;
private MyService target;
public MyServiceProxy(MyService target, MyAspect aspect) {
this.target = target;
this.aspect = aspect;
}
@Override
public void performTask() {
aspect.beforeTask();
target.performTask();
}
}
代理对象
代理类的实例,是真正注入到容器中的对象。
它持有目标对象的引用,负责拦截方法调用并动态执行增强逻辑。例如:
MyService proxyInstance = new MyServiceProxy(new MyServiceImpl(), new MyAspect());
总结
代理对象并不是在方法调用时才创建,而是在 Bean 初始化阶段就完成了。
这是因为:
保证依赖注入和生命周期管理。
切面逻辑需要覆盖整个生命周期。
方法调用时动态创建会导致性能问题。