目录
测试demo
回顾下三级缓存
循环依赖bean实例化初始化过程
源码解读
实例化myZmTest1
myZmTest1依赖myZmTest2,实例化2去注入
myZmTest2依赖myZmTest1,获取myZmTest1去注入
获取到myZmTest1,继续myZmTest2初始化
myZmTest2初始化完成,继续初始化myZmTest1
总结代码主要调用流
循环依赖+@Validated注解报错解析
报错信息
Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException:
Error creating bean with name 'myZmTest1': Bean with name 'myZmTest1' has been injected
into other beans [myZmTest2] in its raw version as part of a circular reference, but has
eventually been wrapped. This means that said other beans do not use the final version of
the bean. This is often the result of over-eager type matching - consider using
'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example.
核心意思是:创建名为“myZmTest1”的bean时出错:名为“myZmTest1”的bean已作为循环引用的一部分注入到原始版本的其他bean[myZmTest2]中,但最终已被包装。这意味着所述其他bean不使用bean的最终版本
大家都知道虽然在开发过程中,不建议出现循环依赖,但是Spring也做了兜底,提供了三级缓存用来解决循环依赖,但为什么循环依赖的类加了参数校验@Validated注解就解决不了了?因为AOP有代理类?其实不是,当循环依赖的一个类方法里加了@Transactional也会被代理,但不会报错。本文就来探究下这两种AOP的区别以及报错的原因
测试demo
@Service
@Validated
public class MyZmTest1 {
@Autowired
private MyZmTest2 myZmTest2;
public void test1(@NonNull Integer id){
System.out.println("MyZmTest1 run..." + id);
}
}
@Service
public class MyZmTest2 {
@Autowired
private MyZmTest1 myZmTest1;
public void test2(){
System.out.println("MyZmTest2 run...");
}
}
回顾下三级缓存
- 第一级缓存:singletonObjects,用于保存实例化、注入、初始化完成的bean实例;
- 第二级缓存:earlySingletonObjects,用于保存实例化完成的bean实例
- 第三级缓存:singletonFactories,用于保存bean创建工厂,以便后续创建代理对象
核心逻辑
- 先从一级缓存去获取,存在就返回,
- 不存在就去二级缓存中去获取, 存在就返回,
- 不存在就去找三级缓存,存在就将bean放入二级缓存中并清空三级缓存
循环依赖bean实例化初始化过程
源码解读
代码入口:org.springframework.context.support.AbstractApplicationContext#refresh
refresh -> finishBeanFactoryInitialization -> preInstantiateSingletons(实例化单例对象)
实例化myZmTest1
图示第一层
调用getBean方法,定位到myZmTest1
进入getBean方法,调用doGetBean
进入 doCreateBean方法后,干两件事,先调用addSingletonFactory,将myZmTest1工厂放入第三级缓存
第二件事,调用populateBean,注入myZmTest2对象
进入到org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#postProcessProperties
获取到依赖属性myZmTest2
myZmTest1依赖myZmTest2,实例化2去注入
图示第二层
最终是根据org.springframework.beans.factory.support.AbstractBeanFactory#getBean(java.lang.String)获取myZmTest2的bean
实例化myZmTest2前面与1一致,直到2依赖1需要去注入1
myZmTest2依赖myZmTest1,获取myZmTest1去注入
图示第三层
获取myZmTest1的bean
入方法情况,解循环依赖核心,目前在第三级缓存中已存在myZmTest1与myZmTest2对象
获取到myZmTest1,继续myZmTest2初始化
回到图示第二层
回到myZmTest2注入myZmTest1处
获取到myZmTest1的bean对象,注入到myZmTest2中,依赖完成,此时依赖的是第三级缓存中的myZmTest1
将myZmTest2实例化完成后,获取将实例化完成的且完整的myZmTest2放入到一级缓存中
myZmTest2初始化完成,继续初始化myZmTest1
回到图示第一层
注入完myZmTest2后,将以初始化完成的myZmTest1对象放到第一层缓存中,结束
最终在第一层缓存中,存在myZmTest1与myZmTest2,完成bean初始化
总结代码主要调用流
循环依赖+@Validated注解报错解析
报错位置:org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean,实例化+初始化bean后还没有将对象放入第一层缓存之前的校验