还记得上一篇笔记,在 bean 加载流程,在创建过程中,出现了依赖循环的监测,如果出现了这个循环依赖,而没有解决的话,代码中将会报错,然后 Spring 容器初始化失败。
由于感觉循环依赖是个比较独立的知识点,所以我将它的分析单独写一篇笔记,来看下什么是循环依赖和如何解决它。
Table of Contents generated with DocToc
- 前言
- 循环依赖构造器循环依赖property 范围的依赖处理setter 循环依赖代码分析解决场景结合关键代码梳理流程创建原始 beanaddSingleFactorypopulateBean 填充属性getSingleton
- 总结
- 参考资料
循环依赖
循环依赖就是循环引用,就是两个或者多个 bean 相互之间的持有对方,最后形成一个环。例如 A 引用了 B,B 引用了 C,C 引用了 A。
可以参照下图理解(图中展示的类的互相依赖,但循环调用指的是方法之间的环调用,下面代码例子会展示方法环调用):
![2aea867e481be3313d338d2f162864ee.png](https://img-blog.csdnimg.cn/img_convert/2aea867e481be3313d338d2f162864ee.png)
如果学过数据库的同学,可以将循环依赖简单的理解为死锁,互相持有对方的资源,形成一个环,然后不释放资源,导致死锁发生。
在循环调用中,除非出现终结条件,否则将会无限循环,最后导致内存溢出错误。(我也遇到过一次 OOM,也是无限循环导致的)
书中的例子是用了三个类进行环调用,我为了简单理解和演示,使用了两个类进行环调用:
在 Spring 中,循环依赖分为以下三种情况:
构造器循环依赖
![9c3aab66878bbf32951172dc6ebf712d.png](https://img-blog.csdnimg.cn/img_convert/9c3aab66878bbf32951172dc6ebf712d.png)
通过上图的配置方法,在初始化的时候就会抛出 BeanCurrentlyInCreationException 异常
public static void main(String[] args) {// 报错原因: Requested bean is currently in creation: Is there an unresolvable circular reference?ApplicationContext context = new ClassPathXmlApplicationContext("circle/circle.xml");}
从上一篇笔记中知道,Spring 容器将每一个正在创建的 bean 标识符放入一个 “当前创建 bean 池(prototypesCurrentlyInCreation)” 中,bean 标识符在创建过程中将一直保持在这个池中。
检测循环依赖的方法:
分析上面的例子,在实例化 circleA 时,将自己 A 放入池中,由于依赖了 circleB,于是去实例化 circleB,B 也放入池中,由于依赖了 A,接着想要实例化 A,发现在创建 bean 过程中发现自己已经在 “当前创建 bean” 里时,于是就会抛出 BeanCurrentlyInCreationException 异常。
如图中展示,这种通过构造器注入的循环依赖,是无法解决的。
property 范围的依赖处理
property 原型属于一种作用域,所以首先来了解一下作用域 scope 的概念:
在 S