Spring 循环依赖及解决方式

循环依赖

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(); //实例化A	a.setB(getBean("B")); //设置属性,发现a依赖于b,所以先加载b,加载B完成以后再继续加载a	a.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 仅仅经过了实例化,并没有设置属性。流程如下:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值