几句话告诉你,spring 如何解决循环依赖问题

1、两种情况,循环依赖无解

在Spring IoC的使用场景中有两类循环依赖是无解的:

1.1 构造器的循环依赖
构造器要调用构造函数new 一个对象出来,而参数又依赖于另一个对象。创建类A依赖于类B,new 的时候去创建类B发现类B不存在就会出错拋出 BeanCurrentlyInCreationException 异常。

1.2 prototype 原型bean循环依赖
原型bean的初始化过程中不论是通过构造器参数循环依赖还是通过set方法产生的循环依赖也会抛出异常。

2、一种情况,循环依赖可解

singleton bean的循环依赖
这个场景可以通过三级缓存的方式解决。
在整理Spring IoC处理singleton bean循环依赖的思路之前先来复习一下bean的生命周期,其包括的三个步骤:

实例化:执行了bean的构造方法,bean中依赖的对象还未赋值

设置属性:给bean中依赖的对象赋值,若被依赖的对象尚未初始化,则先进行该对象的生命周期(递归)。

初始化:执行bean的初始化方法,回调方法等。

解决循环依赖的思路就藏在这三个步骤中,在实例化与设置属性两个步骤之间引入缓存机制,将已经创建好实例但是并没有设置属性的bean放到缓存里,缓存中是没有属性设置的实例对象。假设A对象和B对象互相依赖,A对象的创建需要引用到B对象,而B对象的创建也需要A对象。在创建A对象的时候可以将其放入到缓存中,当B对象创建的时候直接从缓存里引用A对象(此时的A对象只完成了实例化,没有进行设置属性的操作,因此不是完成的A对象,我们称之为半成品A对象),当B对象利用这个半成品的A对象完成实例创建以后(三个步骤都完成),再被A对象引用进去,则A对象也完成了创建。

上文提到的缓存在这里做一个解释,我们将其分为三级,每级缓存都起到不同的作用,如下表格所示:

一级缓存:用于存放完全初始化好的 bean,也就是完成三个步骤的bean,拿出来的bean是可以直接使用的。

二级缓存:存放原始的 bean 对象,此时的对象只进行了实例化但是没有填充属性,也就是我们所说的“半成品对象”,它的建立是用来解决循环依赖的。

三级缓存:用来存放 bean 工厂对象,这个工厂对象是用来产生bean对象的实例的。

解决循环依赖的整个过程是:

先从一级缓存里取bean实例,如果没有对应的bean实例,二级缓存里取,如果二级缓存中也没有bean实例,singletonFactories三级缓存里获取。由于三级缓存存放着产生bean实例的工厂类,因此可以通过该工厂类产生bean实例。

这里可以调用工厂类暴露的getObject方法返回早期暴露对象引用,也是我们所说的半成品bean,也可以成为earlySingletonObject。并且将这个半成品bean放到二级缓存里,在三级缓存里删除该bean。什么时候这个半成品填充了属性以后,就被移动到一级缓存中,也就是被作为可以使用的已经完成初始化的实例bean了,处理循环依赖的过程宣告完毕。下面通过一个例子让大家更好理解这个思路。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值