循环依赖
Spring 有一个经典的问题,就是如何解决循环依赖,话不多说,直接开始,
@Componentpublic Class A {@Autowired private B b;}@Componentpublic Class B {@Autowired private A b;}
复制代码
spring bean 的生命周期

获取一个 Bean 的操作从 getBean(String name) 开始主要步骤为
1、getBean(String name)
2、实例化对象 A a = new A(); 此时执行构造方法的依赖注入
3、设置对象属性 populateBean(beanName, mbd, instanceWrapper); 此时执行属性的依赖注入
4、执行初始化方法 initializeBean(beanName, exposedObject, mbd); 此时执行 bean 的 initialize 方法
5、将生成好的 bean 对象添加到 单例池(一个 hashMap,保证单例 bean 在 context 仅仅存在一个对象)
6、结束
伪代码如下:
public Object getBean(String name) {//省略根据name获取A的过程A a = new A();a.initialze();singletonObjects.put(name, a);return a;}
复制代码
A 依赖 B 的情况下的加载流程

伪代码如下:
public Object getBean(String name) {//省略根据name获取A的过程A a = new A(); //实例化Aa.setB(getBean("B")); //设置属性,发现a依赖于b,所以先加载b,加载B完成以后再继续加载aa.initialze(); //执行初始化方法singletonObjects.put(name, a); //将a放入单例池中return a;}
复制代码
A、B 互相依赖的加载流程

以上就会出现一个问题,由于 a、b 都是单例 Bean,加载 b 的时候,到了上图中标红的阶段后,b 依赖注入的 a 的引用应该是通过 getBean(A) 得到的引入,如果还是以上的逻辑,又再一次走入了 A 的创建逻辑,此时就是发生了循环依赖。下面我们就开始介绍 Spring 是如何解决循环依赖的。
一级缓存:单例池 singletonObjects
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
复制代码
我们都知道如果是单例的 Bean,每次 getBean(beanName)返回同一个 bean,也就是在整个 ApplicationContext 里面,仅有一个单例 Bean,单例 Bean 创建完成后就放在 singletonObjects 这个 Map 里面,这就是一级缓存。此时说的“创建完成”指的是图一的第 6 步骤,图三中 getBean("B") 的过程中,a 是没有加入到一级缓存中,所以在 getBean("B") 的流程中,b 依赖了 a,此时 b 是找不到 a 对象的。依然会无法解决循环引用的问题。
二级缓存:earlySingletonObjects
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
复制代码
这个时候我们考虑再引入一个 Map 存放引用,earlySingletonObjects 这个 map 我们打算存放提前暴露 bean 的引用,实例化以后,我们就把对象放入到 earlySingletonObjects 这个 map 中,这样在 加载 b 的过程中,b.setA(getBean("a")),我们就可以在 earlySingletonObjects 拿到 a 的引用,此时 a 仅仅经过了实例化,并没有设置属性。流程如下:

最低0.47元/天 解锁文章
1万+

被折叠的 条评论
为什么被折叠?



