1.什么是循环依赖?
Bean A → Bean B → Bean A
在A对象生命周期中注入B,进入B生命周期找A,但A还不存在,继续进入A对象生命周期找B,这就是循环依赖。
2.循环依赖造成的结果
当Spring正在加载所有Bean时,Spring尝试以能正常创建Bean的顺序去创建Bean。这样会抛出异常。
┌─────┐
| testA defined in class path resource [com/chuan/config/TestConfig.class]
↑ ↓
| testB defined in class path resource [com/chuan/config/TestConfig.class]
└─────┘
3.逻辑分析
用代码看就是这样:但这只是普通的java代码,而不是spring的bean生命周期。
//实例化A
A a =new A();
//填充A属性,需要注入B,所以进入B的生命周期
B b =new B();
//b实例拿到了a的原始对象,注入进去
b.a=a;
//填充B属性
b.x=1;
//b生命周期结束,回到A的属性填充,填充A属性
a.b=b;
//填充A其他属性
a.x=1;
Bean的生成步骤如下(简化):
- 根据推断出来的构造方法,反射,得到一个对象(暂时叫做原始对象)
- 填充原始对象中的属性(依赖注入)
- 如果原始对象中的某个方法被AOP了,那么则需要根据原始对象生成一个代理对象
- 把最终生成的代理对象放入单例池(源码中叫做singletonObjects)中,下次getBean时就直接从单例池拿即可
暂时先一个demo分析一下怎么解决循环依赖:
demo1 A依赖于B,B依赖于A
A生命周期
1.实例化A(new A())--->a的原始对象--->存在一个map中
2.填充b属性-->从单例池里拿B对应的bean-->没拿到B--->创建B对应的Bean
3.填充其他属性
4.初始化后
5.添加到单例池
B生命周期
1.实例化B(new B())--->b的原始对象
2.填充a属性-->从单例池里拿a对应的bean--->找不到-->去这个map中找-->拿到a的原始对象
3.填充其他属性
4.初始化后
5.添加到单例池
这样来看两级循环就可以完成,一级缓存为单例池,二级缓存存生成的原始对象。
那为什么需要三级缓存呢?——只有两级缓存会存在代理对象和注入的对象不是同一个,这肯定是不合理的。
demo2
看一下demo这个流程:
A依赖于B、C
B依赖于A
C依赖于A
A生命周期
0.标记一下A正在进行创建
1.实例化A(new A())--->a的原始对象--->存入到第三级缓存
2.填充b属性-->从单例池里拿B对应的bean-->找不到-->创建B对应的Bean
3.填充c属性--->从单例池里拿C对应的bean-->找不到-->创建C对应的Bean
4.初始化后-》进行aop,从第二级缓存取出a代理对象
5.将代理对象添加到单例池(第一级缓存)
B生命周期
1.实例化B(new B())--->b的原始对象-->.........
2.填充a属性-->从单例池里拿a对应的bean--->找不到--》第二级缓存--->找不到---->发现a正在创建(也就是发现了需要循环依赖)---->第三级缓存---->执行lambda--->
进行aop--->获取a的代理对象---->存入到第二级缓存,并删除第三级缓存。
3.填充其他属性
4.初始化后
5.添加到单例池
C生命周期
1.实例化C(new C())--->c的原始对象-->.........
2.填充a属性-->从单例池里拿a对应的bean--->找不到--》第二级缓存--->获取到a代理对象(因为先执行了B,已经将第二级缓存缓存进去了)
3.填充其他属性
4.初始化后
5.添加到单例池
4.源码分析
在doCreateBean方法中,看这段代码:
// 如果当前创建的是单例bean,并且允许循环依赖,并且还在创建过程中,那么则提早暴露
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isTraceEnabled()) {
logger.trace("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
// 此时的bean还没有完成属性注入,是一个非常简单的对象
// 构造一个对象工厂添加到singletonFactories中,这时只是存一下第三级缓存
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); // AService
// addEarlySingleton(beanName, bean);
}
只是将第三级缓存存进了singletonFactories里,并没有执行,因为它也不知道会不会出现循环依赖。
向下继续看,执行完属性填充和初始化后,一直到这。
if (earlySingletonExposure) {
// 在解决循环依赖时,当a的属性注入完了之后,从缓存中中得到a AOP之后的代理对象
Object earlySingletonReference = getSingleton(beanName, false); // earlySingletonObjects
if (earlySingletonReference != null) {
// 如果提前暴露的对象和经过了完整的生命周期后的对象相等,则把代理对象赋值给exposedObject
// 最终会添加到singletonObjects中去
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
}
// 如果提前暴露的对象和经过了完整的生命周期后的对象不相等
// allowRawInjectionDespiteWrapping表示在循环依赖时,只能
else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
String[] dependentBeans = getDependentBeans(beanName);
Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
for (String dependentBean : dependentBeans) {
if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
actualDependentBeans.add(dependentBean);
}
}
if (!actualDependentBeans.isEmpty()) {
// a的原始对象被注入给了其他bean,但是a最后被包装了
// 也就是说其他bean没有用到a的最终版本
throw new BeanCurrentlyInCreationException(beanName,
"Bean with name '" + beanName + "' has been injected into other beans [" +
StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
"] 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 " +
"'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
}
}
}
}
进入getSingleton方法查看,如何进行获取三级缓存中的内容。
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
//从单例池(1级缓存)中拿bean
Object singletonObject = this.singletonObjects.get(beanName);
//没有从单例池获取到,并且这个单例正在创建中
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
// 从earlySingletonObjects(二级缓存)中获取
singletonObject = this.earlySingletonObjects.get(beanName);
//没有从二级缓存中获取到,并且这个对象存在早期引用
if (singletonObject == null && allowEarlyReference) {
// 从singletonFactories(三级缓存)中获取
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
//执行之前未执行的lambda表达式,获取到一个对象
singletonObject = singletonFactory.getObject();
//将三级缓存的对象放入到二级缓存中,清除该beanName的三级缓存
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
5.特殊情况
有两个情况循环依赖是不能解决的:
1.两个都是原型bean也不能依赖
因为每次都是重新创建一个新的bean,所以出现循环 A->B->A->B->A
解决方法:将其中一个作用域改为单例
2.两个都使用构造方法注入也不行
因为第三级缓存是在实例化后进行的,所以在构造方法上注入,会导致三层缓存都找不到相应的bean,从而无限循环。
解决方法:在其中一个上加懒加载
6.总结
总结一下三级缓存:
- singletonObjects:缓存某个beanName对应的经过了完整生命周期的bean
- earlySingletonObjects:缓存提前拿原始对象进行了AOP之后得到的代理对象,原始对象还没有进行属性注入和后续的BeanPostProcessor等生命周期
- singletonFactories:缓存的是一个ObjectFactory,主要用来去生成原始对象进行了AOP之后得到的代理对象,在每个Bean的生成过程中,都会提前暴露一个工厂,这个工厂可能用到,也可能用不到,如果没有出现循环依赖依赖本bean,那么这个工厂无用,本bean按照自己的生命周期执行,执行完后直接把本bean放入singletonObjects中即可,如果出现了循环依赖依赖了本bean,则另外那个bean执行ObjectFactory提交得到一个AOP之后的代理对象(如果有AOP的话,如果无需AOP,则直接得到一个原始对象)。