spring容器之bean创建循环依赖处理
还是回到我们创建bean的#doCreateBean(...)方法中,上节我们简单的了解了对于该方法创建完成的bean总是会对其进行属性的填充,这篇我们来了解下该方法中第3个过程,对于循环依赖的处理过程,首先我们得了解下什么是循环依赖
循环依赖
所谓循环依赖就是两个或者两个以上的引用相互引用,最终会形成一个闭环,比如:A依赖B,B依赖C,C依赖A如图所示:

图中更能直接反应循环依赖的过程,实质是一个死循环的过程,是没法终止的过程,除非强制性终止,在图中当spring初始化A实例时,发现需要引用B实例,所以先初始化实例B,发现需要先初始化实例C,大致就是这样
spring中循环依赖的场景
- 单例场景下的循环依赖
- 原型(prototype)场景下的循环依赖
不知道大家记得在我们的spring容器之开启bean的创建之旅这篇文章中,关于bean的创建过程中,我们说过,spring对于依赖处理只针对于单例的情况下,在原型的情况下spring会统一抛出BeanCurrentlyInCreationException异常,所以在接下来的学习中,我们来看spring对于单例情况下的循环依赖处理:
获取单例实例
我们先从原始的创建bean的入口开始,也就是#doGetBean(final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)该方法,该方法位于AbstractBeanFactory.java类中.
在doGetBean(...)方法中有这样一段代码值得注意:
AbstractBeanFactory.java
Object sharedInstance = getSingleton(beanName);
简单的一行代码,其主要的作用是通过指定的beanName去获取单例bean,在缓存中存在的情况下,直接返回即可,接着看:
DefaultSingletonBeanRegistry.java
/**
*
* @param beanName 转化之后的bean的beanName
* @param allowEarlyReference 允许早期依赖
* @return
*/
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
//通过beanName从缓存中去拿对应的实例
Object singletonObject = this.singletonObjects.get(beanName);
//如果为null且当前的单例的bean正在创建过程中
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
//锁定该全局变量singletonObjects进行相关的处理
synchronized (this.singletonObjects) {
//从早期单例bean缓存中去获取
singletonObject = this.earlySingletonObjects.get(beanName);
//如果为null,且提前创建
if (singletonObject == null && allowEarlyReference) {
//从单例工厂中获取
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
//调用getObject并获取对应的实例
singletonObject = singletonFactory.getObject();
//保存到earlySingletonObjects缓存中
this.earlySingletonObjects.put(beanName, singletonObject);
//同时移除singletonFactories中的对应的objectFactory
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
该方法主要是从三种缓存中分别获取,关于三个缓存的讲解我们在之前的文章已经详细的说过了,不清楚的可以去看看.
- singletonObjects是单例对象的缓存
- singletonFactories是单例对象工厂的缓存
- earlySingletonObjects主要的作用是提前曝光单例对象的缓存
在上述代码中我们看到有两个判断的条件分别是:
- isSingletonCurrentlyInCreation(beanName)
- allowEarlyReference变量
关于isSingletonCurrentlyInCreation(beanName):其主要的作用是判断当前bean是否在创建的过程中,如果返回为true则表示我们的bean正在初始化且没完成初始化,在spring中是有专门的属性来记录此刻bean的状态,且不同scope的bean有不同的状态,这也是spring在解决单例bean的过程中为什么会选择提前曝光创建的bean
allowEarlyReference:从字面意思上面理解就是允许提前拿到引用.其实真正的意思是,是否允许从 singletonFactories 缓存中通过 #getObject() 方法,拿到对象。为什么会有这样一个字段呢?原因就在于 singletonFactories 才是 Spring 解决 singleton bean 的诀窍所在(借鉴别人的,咋也不是很清楚)
知道了上面这两个变量用途之后,我们得想一个问题,既然我们是从这三个缓存中获取单例bean的,那么缓存的中的bean是从何而来,是什么时候加载进去的呢?这是我们需要的考虑的地方,其实不然在我们的#doCreateBean(...)方法中有这样一段代码:
AbstractAutowireCapableBeanFactory.java
//4.对单例模式下的循环依赖进行检查
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并加入到addSingletonFactory中
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
说实话,代码不难,但是却耐人寻味,这段代码究竟干了什么其实我也不是很懂,只知道当一个bean满足上面所给的这三个条件时:
- 是一个单例bean
- 允许引用提前曝光的bean
- 当前bean正在初始化的过程中
最后通过方法#addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory)方法完成依赖bean的添加,何时加入的.就是在这里,我们来看看该方法的实现过程:
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);
}
}
}
可以看到,这里就是spring提早曝光创建的bean,可能此时的bean不是成熟的bean,也不是我们需要的bean,之所以spring这样搞,是让我们对这些bean在完成创建之前有一个认识的过程,到这里基本上就大概的了解了循环依赖何时处理何时暴露这些bean的....
小结
这样我们还是简单的以AB循环依赖为case进行分析,我们的类A中拥有属性B,且类B中包含属性A,在初始化Abean的过程中如图所示:

- 从图中可以看到,在创建A时,记录A的beanName属性,之后暴露A.
- 在对A进行属性填充的过程中,发现A依赖B,此时先完成B的初始化过程,最后加入到addSingletonFactory(...)并暴露自己.
- 当在一次属性填充时,由于B中依赖于A,那么在一次的会初始化B,之后调用getBean(A)是从缓存中去检测是否已经有创建好的Abean,或者是ObjectFactory类型的bean,(这里我们在初始化类A时,它的ObjectFactory已经完成了创建),接着调用ObjectFactory的getBean()去创建A.
这就是上图中AB循环依赖的过程,关于本篇的知识点也就到这里了....