一、什么是循环依赖
如果类A存在属性类B,而类B也有属性类A,那么当进行属性的依赖注入时,就会出现A还未完成创建,又由于在创建B的过程中又发生创建A的过程,造成了死循环,最终导致循环依赖。类似代码如下:
public class A {
private B b;
}
public class B {
private A a;
}
循环依赖只会出现在单例实例无参构造函数实例化情况下。有参构造函数的加@Autowired 的方式循环依赖是直接报错的,多例的循环依赖也是直接报错的。接下来我会通过源代码一一说明。
二、循环依赖的步骤
前面几章中,我重点跟了Spring Bean的实例化源码过程,总结一下就是:
- 【Spring源码学习】Spring Bean实例化过程-创建Bean实例
- 【Spring源码学习】Spring Bean实例化过程-依赖属性注入
- 【Spring源码学习】Spring Bean实例化过程-初始化
循环依赖步骤:
- A 类无参构造函数实例化后,设置三级缓存
- A 类populateBean 进行依赖注入,这里触发了B 类属性的getBean 操作
- B 类无参构造函数实例化后,设置三级缓存
- B 类populateBean 进行依赖注入,这里触发了A 类属性的getBean 操作
- A 类之前正在实例化,singletonsCurrentlyInCreation 集合中有已经有这个A 类了,三级缓存里面也有了,所以这时候是从三级缓存中拿到的提前暴露的A 实例,该实例还没有进行B 类属性的依赖注入的,B 类属性为空,具体通过
这个方法(先从一级缓存中拿,再从二级缓存中拿,如果还拿不到,并且允许bean提前暴露则从三级缓存中拿到对象工厂,从工厂中拿到对象成功后,升级到二级缓存,并删除三级缓存。再有别的类引用的话,就从二级缓存中进行取)Object sharedInstance = getSingleton(beanName);
- B 类拿到了A 的提前暴露实例注入到A 类属性中了
- B 类实例化已经完成,B 类的实例化是由A 类实例化中B 属性的依赖注入触发的getBean 操作进行的,现在B已经实例化,所以A 类中B 属性就可以完成依赖注入了,这时候A 类B 属性已经有值了。
- B 类A 属性指向的就是A 类实例堆空间,所以这时候B 类A 属性也会有值了。
三、为什么有参构造函数的加@Autowired 的方式循环依赖是直接报错的?
首先说明一点,构造方法的执行顺序是在依赖注入属性之前的。
创建bean 的实例是在方法createBeanInstance(beanName, mbd, args)中通过无参或注解@Autowired 有参构造函数实例化进行的。
//AbstractAutowireCapableBeanFactory protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { // Instantiate the bean. BeanWrapper instanceWrapper = null; if (mbd.isSingleton()) { instanceWrapper = this.factoryBeanInstanceCache.remove(beanName); } if (instanceWrapper == null) { //创建实例,,重点看,重要程度:5 instanceWrapper = createBeanInstance(beanName, mbd, args); } Object bean = instanceWrapper.getWrappedInstance(); Class<?> beanType = instanceWrapper.getWrappedClass(); if (beanType != NullBean.class) { mbd.resolvedTargetType = beanType; } // Allow post-processors to modify the merged bean definition. //......省略部分代码 // Eagerly cache singletons to be able to resolve circular references // even when triggered by lifecycle interfaces like BeanFactoryAware. //是否单例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"); } //这里着重理解,对理解循环依赖帮助非常大,重要程度 5 添加三级缓存 addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); } // Initialize the bean instance. Object exposedObject = bean; try { //ioc di,依赖注入的核心方法,该方法必须看,重要程度:5 populateBean(beanName, mbd, instanceWrapper); //bean 实例化+ioc依赖注入完以后的调用,非常重要,重要程度:5 exposedObject = initializeBean(beanName, exposedObject, mbd); } //......省略部分代码 // Register bean as disposable. try { //注册bean销毁时的类DisposableBeanAdapter registerDisposableBeanIfNecessary(beanName, bean, mbd); } catch (BeanDefinitionValidationException ex) { throw new BeanCreationException( mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex); } return exposedObject; }
createBeanInstance 方法执行完毕之后才会判断是否是单例bean 提前暴漏从而为实例化的bean 添加三级缓存singletonFactories
//是否单例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");
}
//这里着重理解,对理解循环依赖帮助非常大,重要程度 5 添加三级缓存
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
默认情况下,支持循环依赖,默认都是单例,而正在创建的实例条件是在AbstractAutowireCapableBeanFactory.doCreateBean()的调用方,AbstractBeanFactory.deGetBean()发起的
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// Explicitly remove instance from singleton cache: It might have been put there
// eagerly by the creation process, to allow for circular reference resolution.
// Also remove any beans that received a temporary reference to the bean.
destroySingleton(beanName);
throw ex;
}
});
//该方法是FactoryBean接口的调用入口
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
点进去看看getSingleton().
protected void beforeSingletonCreation(String beanName) {
//把beanName添加到singletonsCurrentlyInCreation Set容器中,在这个集合里面的bean都是正在实例化的bean
if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
}
创建完毕后,会把提前暴露的实例存放至三级缓存中
//DefaultSingletonBeanRegistry
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(singletonFactory, "Singleton factory must not be null");
synchronized (this.singletonObjects) {
//如果一级缓存不存在
if (!this.singletonObjects.containsKey(beanName)) {
//设置三级缓存
this.singletonFactories.put(beanName, singletonFactory);
//删除二级缓存
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
}
如果有参构造函数的有@Autowired ,那么在执行createBeanInstance 方法时,参数为引用类型会再次触发getBean操作。由于之前的bean 的实例化尚未完成,三级缓存中也没法获取到,所以就继续走之前第一次实例化进入的getSingleton 方法,此方法中的beforeSingletonCreation 会校验该bean 是否正在实例化。如果是有参构造函数的有@Autowired的循环依赖的话,则这里的第二个判断为false,因为第一次类A已经在里面了,就会抛异常。
protected void beforeSingletonCreation(String beanName) {
//把beanName添加到singletonsCurrentlyInCreation Set容器中,在这个集合里面的bean都是正在实例化的bean
if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
}
public BeanCurrentlyInCreationException(String beanName) {
super(beanName,
"Requested bean is currently in creation: Is there an unresolvable circular reference?");
}
而对于多例的循环依赖问题,在这里就会抛异常了
//AbstractBeanFactory
//如果是scope 是Prototype的,校验是否有出现循环依赖,如果有则直接报错
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
如果是通过属性加@Autowired 方式进行循环依赖,在进行populateBean 属性依赖注入之前就会把bean 放入三级缓存中,从而进行populateBean 操作时触发getBean 时就可以直接从三级缓存中取就可以了。
总结下三级缓存各自的功能:
缓存 | 说明 |
---|---|
singletonObjects | 第一级缓存,存放可用的成品Bean。 |
earlySingletonObjects | 第二级缓存,存放半成品的Bean,半成品的Bean是已创建对象,但是未注入属性和初始化。用以解决循环依赖。 |
singletonFactories | 第三级缓存,存的是Bean工厂对象,用来生成半成品的Bean并放入到二级缓存中。用以解决循环依赖。 |
四、二级缓存能否解决循环依赖
- 若没有第二级缓存,若有多次依赖某一对象,则每次都需要从三级缓存中创建该对象,失去了单例的意义。(我要的就是单例,你给我来多例,逗我呢?)
- 若没有第三级缓存,对于一般的对象循环依赖问题可以得到解决。那么就得去改写 AbstractAutowireCapableBeanFactory 的 doCreateBean 的方法了。
//添加到三级缓存
if (earlySingletonExposure) {
if (logger.isTraceEnabled()) {
logger.trace("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
//从三级缓存中取出立刻放入二级缓存
getSingleton(beanName, true);
}
但是,对于AOP代理对象的生成,会违背Spring总体实例化对象的流程,因为代理对象是在初始化bean完全之后,进行该bean的代理。即:
Spring结合AOP跟Bean的生命周期,是在Bean创建完全之后通过AnnotationAwareAspectJAutoProxyCreator这个后置处理器来完成的,在这个后置处理的postProcessAfterInitialization方法中对初始化后的Bean完成AOP代理。
若没有第三级缓存,则创建完bean,就得立马创建代理对象,违背了Spring设计原则。