什么是循环依赖?
多个bean之间相互依赖,形成了一个闭环
我们都知道,spring中通过ioc容器,通过反射机制,控制反转,帮我们创建了对象;那么,现在我们要创建一个bean A,但是bean A 依赖的bean B ,而bean B 中又去依赖了bean A,例如下面的构造
class A {
//依赖b
private B b;
public A (B b) {
this.b = b;
}
}
class B {
//依赖a
private A a;
public B (A a) {
this.a = a;
}
}
那么a的创建,需要等待b的创建,b的创建 又需要等待a的创建 (禁止套娃!),因此就出现所谓了循环依赖问题
Spring 中提供的解决策略
循环依赖的报错是 :BeanCurrentlylnCreationException
spring中提出 我们AB循环依赖问题只要A的注入方式是setter且singleton, 就不会有循环依赖问题 ;默认单例,修改为原型scope=“prototype” 就导致了循环依赖错误
本质上是依赖三级缓存机制,解决循环依赖问题
Spring 的三级缓存
循环依赖问题的解决,依赖于三级缓存
查看下源码 DefaultSingletonBeanRegistry
一级缓存 singletonObjects
采用的数据结构是 ConcurrentHashMap;
private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256);
第一级缓存〈也叫单例池)singletonObjects:存放已经经历了完整生命周期的Bean对象(实例化完成- > 初始化完成 )
二级缓存 earlySingletonObjects
采用的数据结构是 ConcurrentHashMap
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap(16);
第二级缓存: earlySingletonObjects,存放早期暴露出来的Bean对象,Bean的生命周期未结束(属性还未填充完整)
(实例化完成- > 初始化未完成)
三级缓存 singletonFactories
采用的数据结构是 HashMap
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap(16);
第三级缓存: Map<String, ObiectFactory<?>> singletonFactories,存放可以生成Bean的工厂
解决循环依赖的底层原理
拿上面的AB bean说明 ,创建 A 实例
主要留意下面的四个方法
下面先来一个大致流程说明
- 首先,我们要创建对象 A的 ,获取到 A 的实例
通过 getSingleton() 去各级缓存中查找, 对象还没创建,缓存中肯定没有 - A实例未被创建 ,调用doCreateBean 创建 A的实例
先实例化A对象 (分配内存空间),将这个 还没初始化的对象,和他的初始化lamda表达式,缓存在第三级缓存singletonFactories中 - 开始A对象的属性B注入 调用 populateBean() , 通过 getSingleton() 从各级缓存中查找 B的实例对象 ,没有,那么开始去创建 B的实例对象
- 调用doCreateBean 创建 B实例 ,B依赖A ,调用 populateBean() ,又通过 getSingleton() 去各级缓存中查找 A的对象实例 ,这一次,在singletonFactories找到了A的实例缓存,成功实例化了 B ,调用addSingleton()将B缓存到第一级缓存 singletonObjects ,并且将 A 从第三级缓存singletonFactories 移动到 第二级缓存earlySingletonObjects 中 ,执行其中的lamda表达式,继续进行A的实例化
- 接下来,A继续去完成自己的属性注入 populateBean() ,getSingleton() 从第一级缓存中找到了 B 的实例对象 ,初始化成功,调用addSingleton()从第二级的缓存移动到第一级别的缓存
- 将A实例对象返回
下面还是画图,梳理明白
脑图的地址 https://www.processon.com/view/link/5ffea7f507912914e7e82a57
大家要是先进一步的学明白,还是要通过自己debug打断点,一步一步吃透