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
对这两种循环依赖的处理并无不同,所以这里不再单独对间接循环依赖举例。
Spring
对bean
循环依赖的解决
接下来我们结合Spring
对单例bean
统一的获取过程和有关的数据结构分析Spring
对单例bean
循环依赖的解决。
Spring
对单例bean
统一的获取过程
Spring
对单例bean
的统一的获取过程为 :
// 类 AbstractBeanFactory
#getBean
=> #doGetBean
而方法#doGetBean
主要流程如下 :
getSingleton(beanName,true)
- 如果
singletonObjects
包含beanName
对应单例bean
实例则返回之; - 如果
earlySingletonObjects
包含beanName
对应单例bean
实例则返回之; - 如果
singletonFactories
包含beanName
对应单例bean
实例工厂则:
- 从对应单例
bean
实例工厂获取单例bean
实例; - 将所获取的单例
bean
实例放到earlySingletonObjects
; - 从
singletonFactories
删除对应的单例bean
实例工厂;
如果该步骤能获取到单例实例,则直接返回不再继续;
getSingleton(String beanName, () -> {createBean(...)})
- 标记单例实例处于创建过程中
createBean
- 应用
InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation
doCreateBean
-
createBeanInstance
创建对象 - 应用
MergedBeanDefinitionPostProcessors
addSingletonFactory(beanName, () -> getEarlyBeanReference(...))
-
populateBean
填充属性,依赖注入
这里会递归触发所依赖的
bean
的创建
-
initializeBean
初始化
- 标记单例实例创建结束
- 将新建单例实例正式登记到单例注册表
- 将所创建的单例
bean
实例放到singletonObjects
; - 从
earlySingletonObjects
删除对应的单例bean
实例工厂; - 从
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
开始之前
数据结构 | 对组件 |
| [] |
| [] |
| [] |
#doGetBeanA
执行步骤2.2.3
结束时
数据结构 | 对组件 |
| [] |
| [] |
| [ |
#doGetBeanA
执行到步骤2.2.4
触发所依赖组件BeanB
的获取
这一步骤会触发所依赖组件BeanB
的获取,也就是我们上面我们所定义的过程#doGetBeanB
的执行,#doGetBeanB
开始之前以下数据结构状态保持跟上面相同 :
数据结构 | 对组件 |
| [] |
| [] |
| [ |
#doGetBeanB
执行步骤2.2.3
结束时
数据结构 | 对组件 |
| [] |
| [] |
| [ |
#doGetBeanB
执行步骤2.2.4
触发所依赖组件BeanA
的获取
这一步骤会触发所依赖组件BeanA
的获取,也就是我们上面我们所定义的过程#doGetBeanA
的第二次执行,#doGetBeanA
再次开始之前各数据结构保持跟上面一样 :
数据结构 | 对组件 |
| [] |
| [] |
| [ |
#doGetBeanA
二次执行步骤1.3
结束时
因为#doGetBeanA
第一次执行已经在步骤2.2.3
添加了组件BeanA
的singletonFactory
到singletonFactories
,所以#doGetBeanA
二次执行时会检测到该信息从而使用它直接创建组件BeanA
的及早暴露实例并返回。
这里之所以将这里创建的组件
BeanA
实例称作及早暴露实例,是因为整个#doGetBeanA
过程尚未执行完成就需要将其实例暴露给使用者。
此时各个数据结构的内容如下 :
数据结构 | 对组件 |
| [] |
| [ |
| [ |
一旦获取到组件BeanA
单例实例,#doGetBeanB
将可以继续执行直到#doGetBeanB
执行到2.4
步骤结束。
#doGetBeanB
执行到步骤2.4
结束时
各个数据结构的内容如下 :
数据结构 | 对组件 |
| [ |
| [ |
| [] |
#doGetBeanB
执行到步骤2.4
结束时,也就意味着组件BeanB
的创建结束了,#doGetBeanA
的首次执行可以继续进行了,因为它现在拿到了所需要的依赖BeanB
的单例实例。接下来,它会一直执行直到它的步骤2.4
结束时。
#doGetBeanA
执行到步骤2.4
结束时
各个数据结构的内容如下 :
数据结构 | 对组件 |
| [ |
| [] |
| [] |
到此为止,存在循环依赖的组件BeanA
/BeanB
的单例实例获取就已经圆满完成了,相应的循环依赖属性也得到了正确的处理。