Spring bean循环依赖例子代码

Bean组件A : 依赖于Bean组件B

@Component
public class BeanA {
    BeanB b;

    @Autowired
    public void setB(BeanB b) {
        this.b = b;
    }

    public String describeB() {
        return " ==引用==> BeanB 实例 :" + b;
    }
}

Bean组件B : 依赖于Bean组件A

@Component
public class BeanB {
    BeanA a;

    @Autowired
    public void setA(BeanA a) {
        this.a = a;
    }


    public String describeA() {
        return " ==引用==> BeanA 实例 :" + a ;
    }
}

上面的循环依赖可以通过下图表示 :


属性依赖

属性依赖

beanA

beanB


上面的例子表达的是两个bean之间直接循环依赖,另外还会有两个bean之间间接循环依赖的情况,Spring对这两种循环依赖的处理并无不同,所以这里不再单独对间接循环依赖举例。

Springbean循环依赖的解决

接下来我们结合Spring对单例bean统一的获取过程和有关的数据结构分析Spring 对单例bean循环依赖的解决。

Spring对单例bean统一的获取过程

Spring对单例bean的统一的获取过程为 :

// 类 AbstractBeanFactory
#getBean
=> #doGetBean

而方法#doGetBean主要流程如下 :

  1. getSingleton(beanName,true)
  1. 如果singletonObjects包含beanName对应单例bean实例则返回之;
  2. 如果earlySingletonObjects包含beanName对应单例bean实例则返回之;
  3. 如果singletonFactories包含beanName对应单例bean实例工厂则:
  1. 从对应单例bean实例工厂获取单例bean实例;
  2. 将所获取的单例bean实例放到earlySingletonObjects;
  3. singletonFactories删除对应的单例bean实例工厂;

如果该步骤能获取到单例实例,则直接返回不再继续;

  1. getSingleton(String beanName, () -> {createBean(...)})
  1. 标记单例实例处于创建过程中
  2. createBean
  1. 应用InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation
  2. doCreateBean
  1. createBeanInstance 创建对象
  2. 应用MergedBeanDefinitionPostProcessors
  3. addSingletonFactory(beanName, () -> getEarlyBeanReference(...))
  4. populateBean 填充属性,依赖注入

这里会递归触发所依赖的bean的创建

  1. initializeBean 初始化
  1. 标记单例实例创建结束
  2. 将新建单例实例正式登记到单例注册表
  1. 将所创建的单例bean实例放到singletonObjects;
  2. earlySingletonObjects删除对应的单例bean实例工厂;
  3. singletonFactories删除对应的单例bean实例工厂;

Spring辅助单例bean获取的数据结构

Spring使用了以下数据结构来保存处于不同创建阶段的单例bean实例 :

  • Map<String, Object> singletonObjects 缓存已经创建完成的单例bean对象
  • 实例已经创建,并且已经完成属性设置,依赖注入和初始化
  • Map<String, Object> earlySingletonObjects 缓存尽早暴露的单例bean对象
  • 实例已经创建,还没有完成属性设置,依赖注入和初始化
  • Map<String, ObjectFactory<?>> singletonFactories 缓存单例bean名称和对应的ObjectFactory对象
  • Set<String> singletonsCurrentlyInCreation 当前正在创建过程中的单例bean的名称
  • 正在创建过程中的bean实例

分析组件BeanA/BeanB循环依赖的解决

为了方便描述,这里我们将BeanA单例实例的获取过程标记为#doGetBeanA,将BeanB单例实例的获取过程标记为#doGetBeanB。然后我们从#doGetBeanA的执行开始,结合以上辅助数据结构分析Spring对组件BeanA/BeanB循环依赖的解决。

#doGetBeanA开始之前

数据结构

对组件BeanA/BeanB的信息记录

singletonObjects

[]

earlySingletonObjects

[]

singletonFactories

[]

#doGetBeanA执行步骤2.2.3结束时

数据结构

对组件BeanA/BeanB的信息记录

singletonObjects

[]

earlySingletonObjects

[]

singletonFactories

[beanA的ObjectFactory]

#doGetBeanA执行到步骤2.2.4触发所依赖组件BeanB的获取

这一步骤会触发所依赖组件BeanB的获取,也就是我们上面我们所定义的过程#doGetBeanB的执行,#doGetBeanB开始之前以下数据结构状态保持跟上面相同 :

数据结构

对组件BeanA/BeanB的信息记录

singletonObjects

[]

earlySingletonObjects

[]

singletonFactories

[beanA的ObjectFactory]

#doGetBeanB执行步骤2.2.3结束时

数据结构

对组件BeanA/BeanB的信息记录

singletonObjects

[]

earlySingletonObjects

[]

singletonFactories

[beanA的ObjectFactorybeanB的ObjectFactory]

#doGetBeanB执行步骤2.2.4触发所依赖组件BeanA的获取

这一步骤会触发所依赖组件BeanA的获取,也就是我们上面我们所定义的过程#doGetBeanA的第二次执行,#doGetBeanA再次开始之前各数据结构保持跟上面一样 :

数据结构

对组件BeanA/BeanB的信息记录

singletonObjects

[]

earlySingletonObjects

[]

singletonFactories

[beanA的ObjectFactorybeanB的ObjectFactory]

#doGetBeanA二次执行步骤1.3结束时

因为#doGetBeanA第一次执行已经在步骤2.2.3添加了组件BeanAsingletonFactorysingletonFactories,所以#doGetBeanA二次执行时会检测到该信息从而使用它直接创建组件BeanA的及早暴露实例并返回。

这里之所以将这里创建的组件BeanA实例称作及早暴露实例,是因为整个#doGetBeanA过程尚未执行完成就需要将其实例暴露给使用者。

此时各个数据结构的内容如下 :

数据结构

对组件BeanA/BeanB的信息记录

singletonObjects

[]

earlySingletonObjects

[beanA]

singletonFactories

[beanB的ObjectFactory]

一旦获取到组件BeanA单例实例,#doGetBeanB将可以继续执行直到#doGetBeanB执行到2.4步骤结束。

#doGetBeanB执行到步骤2.4结束时

各个数据结构的内容如下 :

数据结构

对组件BeanA/BeanB的信息记录

singletonObjects

[beanB]

earlySingletonObjects

[beanA]

singletonFactories

[]

#doGetBeanB执行到步骤2.4结束时,也就意味着组件BeanB的创建结束了,#doGetBeanA的首次执行可以继续进行了,因为它现在拿到了所需要的依赖BeanB的单例实例。接下来,它会一直执行直到它的步骤2.4结束时。

#doGetBeanA执行到步骤2.4结束时

各个数据结构的内容如下 :

数据结构

对组件BeanA/BeanB的信息记录

singletonObjects

[beanA,beanB]

earlySingletonObjects

[]

singletonFactories

[]

到此为止,存在循环依赖的组件BeanA/BeanB的单例实例获取就已经圆满完成了,相应的循环依赖属性也得到了正确的处理。