<>前言

你是不是被这个骚气的标题吸引进来的,_ 喜欢我的文章的话就给个好评吧,你的肯定是我坚持写作最大的动力,来吧兄弟们,给我一点动力

Spring如何处理循环依赖?这是最近较为频繁被问到的一个面试题,在前面Bean实例化流程中,对属性注入一文多多少少对循环依赖有过介绍,这篇文章详细讲一下Spring中的循环依赖的处理方案。

<>什么是循环依赖

依赖指的是Bean与Bean之间的依赖关系,循环依赖指的是两个或者多个Bean相互依赖,如:

<>构造器循环依赖

代码示例:
public class BeanA { private BeanB beanB; public BeanA(BeanB beanB){ this.beanB
= beanB; } } public class BeanB { private BeanA beanA; public BeanB(BeanA beanA)
{ this.beanA = beanA; } }
配置文件
<bean id="beanA" class="cn.itsource._01_di.BeanA" > <constructor-arg type="
cn.itsource._01_di.BeanB" ref="beanB" /> </bean> <bean id="beanB" class="
cn.itsource._01_di.BeanB" > <constructor-arg type="cn.itsource._01_di.BeanA" ref
="beanA" /> </bean>
<>Setter循环依赖

代码示例
public class BeanA { private BeanB beanB; public void setBeanB(BeanB beanB){
this.beanB = beanB; } } @Data public class BeanB { private BeanA beanA; public
void setBeanA(BeanA beanA){ this.beanA = beanA; } }
配置文件
<bean id="beanA" class="cn.itsource._01_di.BeanA" > <property name="beanB" ref=
"beanB" /> </bean> <bean id="beanB" class="cn.itsource._01_di.BeanB"> <property
name="beanA" ref="beanA" /> </bean>
循环依赖包括: 构造器注入循环依赖 set , 注入循环依赖 和 prototype模式Bean的循环依赖。Spring只解决了单利Bean的 setter
注入循环依赖,对于构造器循环依赖,和 prototype模式的循环依赖是无法解决的,在创建Bean的时候就会抛出异常
:“BeanCurrentlyInCreationException” ,

循环依赖控制开关在 AbstractRefreshableApplicationContext 容器工厂类中有定义:
public abstract class AbstractRefreshableApplicationContext extends
AbstractApplicationContext { @Nullable private Boolean
allowBeanDefinitionOverriding; //是否允许循环依赖 @Nullable private Boolean
allowCircularReferences; //设置循环依赖 public void setAllowCircularReferences(boolean
allowCircularReferences) { this.allowCircularReferences =
allowCircularReferences; }

默认情况下是允许Bean之间的循环依赖的,在依赖注入时Spring会尝试处理循环依赖。如果将该属性配置为“false”则关闭循环依赖,当在Bean依赖注入的时遇到循环依赖时抛出异常。可以通过如下方式关闭,但是一般都不这么做
ClassPathXmlApplicationContext applicationContext = new
ClassPathXmlApplicationContext("bean.xml"); //禁用循环依赖 applicationContext.
setAllowCircularReferences(false); //刷新容器 applicationContext.refresh(); ...
<>构造器循环依赖处理

构造器是不允许循环依赖的,动动你的小脑瓜想一想,比如:A 依赖 B
,B依赖C,C依赖A,在实例化A的时候,构造器需要注入B,然后Spirng会实例化B,此时的A属于“正在创建”的状态。当实例化B的时候,发现构造器需要注入C,然后去实例化C,然而实例化C的时候又需要注入A的实例,这样就造成了一个死循环,永远无法先实例化出某一个Bean,所以Spring遇到这里构造器循环依赖会直接抛出异常。

那么Spring到底是如何做的呢?

*
首先Spring会走Bean的实例化流程尝试创建 A 的实例 ,在创建实例之间先从 “正在创建Bean池” (一个缓存Map而已)中去查找A
是否正在创建,如果没找到,则将 A 放入 “正在创建Bean池”中,然后准备实例化构造器参数 B。

*
Spring会走Bean的实例化流程尝试创建 B 的实例 ,在创建实例之间先从 “正在创建Bean池” (一个缓存Map而已)中去查找B
是否正在创建,如果没找到,则将 B 放入 “正在创建Bean池”中,然后准备实例化构造器参数 A。

*
Spring会走Bean的实例化流程尝试创建 A 的实例 ,在创建实例之间先从 “正在创建Bean池” (一个缓存Map而已)中去查找A 是否正在创建。

*
此时:Spring发现 A
正处于“正在创建Bean池”,表示出现构造器循环依赖,抛出异常:“BeanCurrentlyInCreationException”

<>DefaultSingletonBeanRegistry#getSingleton

下面我们以 BeanA 构造参数依赖BeanB, BeanB 构造参数依赖BeanA 为例来分析。

当Spring的IOC容器启动,尝试对单利的BeanA进行初始化,根据之前的分析我们知道,单利Bean的创建入口是
AbstractBeanFactory#doGetBean 在该方法中会先从单利Bean缓存中获取,如果没有代码会走到:
DefaultSingletonBeanRegistry#getSingleton(jString beanName, ObjectFactory<?>
singletonFactory) 方法中 ,在该方法中会先对把创建的Bean加入 一个名字为 singletonsCurrentlyInCreation 的
ConcurrentHashMap中,意思是该Bean正在创建中,然后调用 ObjectFactory.getObject() 实例化Bean , 假设
BeanA 进入了该方法进行实例化:
//正在创建中的Bean private final Set<String> singletonsCurrentlyInCreation =
Collections.newSetFromMap(new ConcurrentHashMap<>(16)); public Object
getSingleton(String beanName, ObjectFactory<?> singletonFactory) { ...省略...
//把该Bean的名字加入 singletonsCurrentlyInCreation 正在创建池 中 beforeSingletonCreation(
beanName); boolean newSingleton = false; boolean recordSuppressedExceptions = (
this.suppressedExceptions == null); if (recordSuppressedExceptions) { this.
suppressedExceptions= new LinkedHashSet<>(); } try { //调用ObjectFactory创建Bean的实例
singletonObject= singletonFactory.getObject(); newSingleton = true; } ...省略...
//如果singletonsCurrentlyInCreation中没该Bean,就把该Bean存储到singletonsCurrentlyInCreation中,
//如果 singletonsCurrentlyInCreation 中有
该Bean,就报错循环依赖异常BeanCurrentlyInCreationException //也就意味着同一个beanName进入该方法2次就会抛异常
protected void beforeSingletonCreation(String beanName) { if (!this.
inCreationCheckExclusions.contains(beanName) && !this.
singletonsCurrentlyInCreation.add(beanName)) { throw new
BeanCurrentlyInCreationException(beanName); } }
beforeSingletonCreation 方法非常关键 ,它会把beanName加入
singletonsCurrentlyInCreation,一个代表“正在创建中的Bean”的ConcurrentHashMap中。

*
如果singletonsCurrentlyInCreation中没该beanName,就把该Bean存储到singletonsCurrentlyInCreation中,
* 如果 singletonsCurrentlyInCreation 中有
该Bean,就报错循环依赖异常BeanCurrentlyInCreationException
【注意】也就意味着同一个beanName进入该方法2次就会抛异常 , 现在BeanA已经加入了singletonsCurrentlyInCreation

<>AbstractAutowireCapableBeanFactory#autowireConstructor

我们前面分析过
ObjectFactory.getObject实例化Bean的详细流程,这里我只是大概在复盘一下就行了。因为我们的BeanA的构造器注入了一个BeanB,所以
代码最终会走到AbstractAutowireCapableBeanFactory#autowireConstructor
,通过构造器来实例化BeanA(在属性注入那一章有讲到 ) 。

在autowireConstructor 方法中会通过 ConstructorResolver#resolveConstructorArguments
来解析构造参数,调用 BeanDefinitionValueResolver 去把ref="beanB"
这种字符串的引用变成一个实实在在的Bean,即BeanB,所以在BeanDefinitionValueResolver
属性值解析器中又会去实例化BeanB,同样会走到DefaultSingletonBeanRegistry#getSingleton 中把BeanB加入
singletonsCurrentlyInCreation “正在创建Bean池”中,然后调用ObjectFactory.getObject实例化BeanB。

低于BeanB而已同样需要通过构造器创建,BeanB构造器参数依赖了BeanA,也就意味着又会调用 BeanDefinitionValueResolver
去把 ref=“beanA” 这种字符串引用变成容器中的BeanA的Bean实例,然后代码又会走到
DefaultSingletonBeanRegistry#getSingleton。然后再一次的尝试把BeanA加入
singletonsCurrentlyInCreation “正在创建Bean池”。

此时问题就来了,在最开始创建BeanA的时候它已经加入过一次“正在创建Bean”
池,这会儿实例化BeanB的时候,由于构造器参数依赖了BeanA,导致BeanA又想进入“正在创建Bean” 池 ,此时 Spring抛出循环依赖异常:

Error creating bean with name ‘beanA’: Requested bean is currently in
creation: Is there an unresolvable circular reference?

到这,Spring处理构造器循环依赖的源码分析完毕。

<>setter循环依赖处理

setter循环依赖是可以允许的。Spring是通过提前暴露未实例化完成的Bean的 ObjectFactory
来实现循环依赖的,这样做的目的是其他的Bean可以通过 ObjectFactory 引用到该Bean。

实现流程如下:

*
Spring创建BeanA,通过无参构造实例化,把BeanA添加到“正在创建Bean池”中,并暴露当前实例的ObjectFactory,即把ObjectFactory添加到singletonFactories(三级缓存)中,该ObjectFactory用来获取创建中的BeanA,然后,然后通过setter注入BeanB
*
Spring创建BeanB,通过无参构造实例化,把BeanB添加到“正在创建Bean池”中,并暴露一个ObjectFactory,然后,然后通过setter注入BeanA
* 在BeanB通过setter注入BeanA时,由于BeanA 提前暴露了ObjectFactory ,通过它返回一个提前暴露一个创建中的BeanA。
* 然后完成BeanB的依赖注入
这里补张图:

获取Bean的时候走三级缓存
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
//一级缓存,存储实例化好的Bean Object singletonObject = this.singletonObjects.get(beanName);
//如果单利缓存池中没有,但是beanName正在创建 if (singletonObject == null &&
isSingletonCurrentlyInCreation(beanName)) { synchronized (this.singletonObjects)
{ //获取二级缓存,这个里面存储的是正在创建的Bean,半成品 singletonObject = this.earlySingletonObjects.
get(beanName); //如果也为空,但是允许循环依赖 if (singletonObject == null &&
allowEarlyReference) { //从三级缓存获取Bean的创建工厂, ObjectFactory<?> singletonFactory =
this.singletonFactories.get(beanName); if (singletonFactory != null) {
//创建Bean的实例 singletonObject = singletonFactory.getObject(); //把Bean存储到二级缓存 this.
earlySingletonObjects.put(beanName, singletonObject); //移除三级缓存中的创建工厂 this.
singletonFactories.remove(beanName); } } } } return (singletonObject !=
NULL_OBJECT? singletonObject : null); }
<>AbstractAutowireCapableBeanFactory#doCreateBean

我们以BeanA 通过settter依赖BeanB,BeanB通过setter
依赖BeanA为例来分析一下源码,在之前的Bean实例化流程分析过程中我们了解到,Bean的实例化会走AbstractBeanFactory#doGetBean
,然后查找单利缓存中是否有该Bean ,如果没有就调用DefaultSingletonBeanRegistry#getSingleton,方法会把BeanA加入
singletonsCurrentlyInCreation “创建中的Bean池”,然后调用ObjectFactory.getObject创建Bean.

org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean 源码:
protected <T> T doGetBean(final String name, @Nullable final Class<T>
requiredType, @Nullable final Object[] args, boolean typeCheckOnly) throws
BeansException { final String beanName = transformedBeanName(name); Object bean;
// Eagerly check singleton cache for manually registered singletons.
//缓存中获取Bean,解决了循环依赖问题 Object sharedInstance = getSingleton(beanName); ...
缓存中没有走下面... if (mbd.isSingleton()) { //走
DefaultSingletonBeanRegistry#getSingleton ,方法会把bean加入“正在创建bean池”
//然后调用ObjectFactory实例化Bean 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; } }); bean = getObjectForBeanInstance(
sharedInstance, name, beanName, mbd); }
第一次进来,缓存中是没有BeanA的,所有会走 getSingleton 方法,然后代码最终会走到
AbstractAutowireCapableBeanFactory#doCreateBean 方法中 。

AbstractAutowireCapableBeanFactory#doCreateBean源码:
protected Object doCreateBean(final String beanName, final RootBeanDefinition
mbd, final @Nullable Object[] args) throws BeanCreationException { //
Instantiate the bean. BeanWrapper instanceWrapper = null; if (mbd.isSingleton())
{ instanceWrapper = this.factoryBeanInstanceCache.remove(beanName); } if (
instanceWrapper== null) { //实例化Bean instanceWrapper = createBeanInstance(
beanName, mbd, args); } ...省略... //如果是单利 ,如果是允许循环依赖,如果 beanName
出于创建中,已经被添加到“创建中的bean池” boolean earlySingletonExposure = (mbd.isSingleton() &&
this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName)); if (
earlySingletonExposure) { if (logger.isDebugEnabled()) { logger.debug("Eagerly
caching bean '" + beanName + "' to allow for resolving potential circular
references"); } //把ObjectFactory 添加到 singletonFactories 中。 addSingletonFactory(
beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); } try { //走依赖注入流程
populateBean(beanName, mbd, instanceWrapper); exposedObject = initializeBean(
beanName, exposedObject, mbd); } //缓存单利Bean的创建工厂,用于解决循环依赖 protected void
addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) { Assert
.notNull(singletonFactory, "Singleton factory must not be null"); synchronized (
this.singletonObjects) { //singletonObjects单利缓存中是否包含Bean if (!this.
singletonObjects.containsKey(beanName)) {
//提前暴露ObjectFactory,把ObjectFactory放到singletonFactories中,
//后面解决循环依赖,获取Bean实例的时候会用到 this.singletonFactories.put(beanName, singletonFactory
); //早期单利bean缓存中移除Bean this.earlySingletonObjects.remove(beanName);
//把注册的Bean加入registeredSingletons中 this.registeredSingletons.add(beanName); } } }
该方法中把BeanA实例化好之后,会把ObjectFactory存储到一个 singletonFactories
(HashMap)中来提前暴露Bean的创建工厂,用于解决循环依赖【重要】,然后调用 populateBean 走属性注入流程。

属性注入会通过BeanDefinition得到bean的依赖属性,然后调用
AbstractAutowireCapableBeanFactory#applyPropertyValues
,把属性应用到对象上。在applyPropertyValues 方法中最终调用
BeanDefinitionValueResolver#resolveValueIfNecessary 解析属性值,比如:ref=“beanB”
这种字符串引用变成 对象实例的引用。

在BeanDefinitionValueResolver解析依赖的属性值即:BeanB的时候,同样会触发BeanB的实例化,代码会走到
AbstractBeanFactory#doGetBean ,然后走方法 DefaultSingletonBeanRegistry#getSingleton
中把BeanB加入singletonsCurrentlyInCreation “创建中的Bean池”,然后代码会走到
AbstractAutowireCapableBeanFactory#doCreateBean 方法中创建BeanB,

该方法中会先实例化BeanB,接着会把BeanB的ObjectFactory存储到 singletonFactories
(HashMap)中来提前暴露Bean的创建工厂,用于解决循环依赖,然后调用 populateBean 走属性注入流程。

同样因为BeanB通过Setter 注入了 A,所以在 populateBean 属性注入流程中会解析 ref=“beanA” 为容器中的 BeanA
的实例。

然后会走到 AbstractBeanFactory#doGetBean 中获取BeanA的实例。这个时候流程就不一样了,我们先看一下
AbstractBeanFactory#doGetBean 中的代码
protected <T> T doGetBean(final String name, @Nullable final Class<T>
requiredType, @Nullable final Object[] args, boolean typeCheckOnly) throws
BeansException { final String beanName = transformedBeanName(name); Object bean;
// Eagerly check singleton cache for manually registered singletons.
//从缓存中获取Bean Object sharedInstance = getSingleton(beanName); ...省略...
//如果缓存中没有Bean,就创建Bean 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; } });
bean= getObjectForBeanInstance(sharedInstance, name, beanName, mbd); }
在获取单利Bean的实例的时候是会先去单利Bean的缓存中去查看Bean是否已经存在
,如果不存在,才会走DefaultSingletonBeanRegistry#getSingleton方法创建Bean。
问题是:此刻单利Bean缓存中已经有BeanA了,因为在最开始BeanA已经出于“正在创建Bean池”中了。我们先来看一下是如何从缓存获取Bean的。

DefaultSingletonBeanRegistry#getSingleton(java.lang.String, boolean)源码如下:
//allowEarlyReference :是否创建早期应用,主要用来解决循环依赖 @Nullable protected Object
getSingleton(String beanName, boolean allowEarlyReference) { // Quick check for
existing instance without full singleton lock //从Map中 singletonObjects = new
ConcurrentHashMap<>(256); 获取单利Bean //【一级缓存】singletonObject缓存中是否有Bean ,
它存储的是已经实例化好的Bean Object singletonObject = this.singletonObjects.get(beanName);
//如果singletonObjects中没有Bean,但是Bean出于正在创建池中,即: Set<String>
singletonsCurrentlyInCreation中有Bean, if (singletonObject == null &&
isSingletonCurrentlyInCreation(beanName)) { //【二级缓存】从早期单例对象的缓存
earlySingletonObjects 中获取 singletonObject = this.earlySingletonObjects.get(
beanName); //早期单利对象缓存中也没有,但是允许循环依赖 if (singletonObject == null &&
allowEarlyReference) { synchronized (this.singletonObjects) { // Consistent
creation of early reference within full singleton lock singletonObject = this.
singletonObjects.get(beanName); if (singletonObject == null) { singletonObject =
this.earlySingletonObjects.get(beanName); if (singletonObject == null) {
//【三级缓存】获取ObjectFactory , 对象创建工厂,得到Bean创建过程中提前暴露的工厂。 ObjectFactory<?>
singletonFactory= this.singletonFactories.get(beanName); if (singletonFactory !=
null) { //通过工厂ObjectFactory 获取对象实例 singletonObject = singletonFactory.getObject(
); //把对象存储到早期缓存中 this.earlySingletonObjects.put(beanName, singletonObject);
//把ObjectFactory移除 this.singletonFactories.remove(beanName); } } } } } } return
singletonObject; }
这里就是经典的三级缓存解决Spring循环依赖。你看到了,这里会先从 singletonObjects
单利Bean缓存集合中获取Bean(该缓存是实例化完成了的Bean),如果没有,就从earlySingletonObjects早期对象缓存中获取Bean(该缓存中存放的是还未实例化完成的早期Bean),如果还是没有,就从singletonFactories中得到暴露的ObjectFactory来获取依赖的Bean。然后放入早期缓存中。并把ObjectFactory从singletonFactories中移除。最后返回Bean的实例。

由于在实例化BeanA的时候已经把BeanA的ObjectFactory添加到了 singletonFactories 缓存中,那么这里就会走到
singletonFactory.getObject(); 方法得到BeanA的实例,并且会把BeanA存储到
earlySingletonObjects早期单利Bean缓存中。

BeanA的实例成功返回,那么BeanB的 setter注入成功,代表BeanB实例化完成,那么BeanA的setter方法注入成功,BeanA实例化完成。

<>prototype模式的循环依赖

对于prototype模式下的Bean不允许循环依赖,因为 这种模式下Bean是不做缓存的,所以就没法暴露ObjectFactory,也就没办法实现循环依赖。

<>总结

不知道你有没有看晕,反正我但是在源码时的过程是比较辛苦的~~~~(>_<)~~~~
,这里需要你对前面Bean的实例化流程和属性注入流程比较熟悉,否则就会晕菜。

这里总结一下:

* 构造器循环依赖是不允许的,主要通过 singletonsCurrentlyInCreation “正在创建Bean池”
把创建中的Bean缓存起来,如果循环依赖,同一个Bean势必会尝试进入该缓存2次,抛出循环依赖异常。
* setter循环依赖是可以允许的。Spring是通过提前暴露未实例化完成的Bean的 ObjectFactory
来实现循环依赖的,这样做的目的是其他的Bean可以通过 ObjectFactory 引用到该Bean 。 在获取依赖的Bean的时候使用到了三级缓存。
下面的面试题你会答了吗?

* Spirng支持那种模式下的循环依赖(构造器?,setter?, prototype?)
* Spring是如何处理构造器注入循环依赖的?
* Spring是如何处理Setter注入循环依赖的?
喜欢我的文章的话就给个好评吧,你的肯定是我坚持写作最大的动力,来吧兄弟们,给我一点动力

技术
©2019-2020 Toolsou All rights reserved,
大一上c语言学生管理系统(下)年底了,不要跳槽。字节跳动测试工程师凉经分享教你用Python画一棵圣诞树用C实现圣诞树python 使用turtle 画樱花(python3验证ok)win10系统的计算机C盘在哪,c盘users在哪(win10c盘找不到users)计算机发展史上最著名的两位鼻祖HDFS主要组件(数据块、NameNode、DataNode、secondaryNameNode)python 指定时间运行代码