spring是如何解决循环依赖的

所谓的循环依赖是指,A 依赖 B,B 又依赖 A,它们之间形成了循环依赖。或者是A 依赖B,B依赖C,C 又依赖A。它们之间的依赖关系如下:

 

一级缓存(singletonObejects):

就是单例池,作用就是限制bean在beanFactory中只有一份,为单例bean。 当使用getBean()的时候,先去单例池中看有没有对应的实例,第一次没有就去创建一个实例对象,然后放入一级缓存,后续就可以直接从一级缓存单例池中拿。 只有一级缓存会在存在循环依赖的时候造成死循环的问题。

三级缓存(singletonFactories):

在只有一级缓存的情况下对代码进行了调整,在getbean()之后创建了对象之后就放入三级缓存中,这时候放入的对象仅仅经历了实例化阶段,后续的依赖注入、初始化还未执行,换句话说就是一个半成品对象。

图中例子:

1、首先单例池没有A对象所以会执行getBean(A)的方法,创建了A的实例对象后会先放入三级缓存中

2、接着走到a.set(B)的时候不是直接去一级缓存中取B,而是先去三级缓存中看看有没有半成品的B,没有再去getBean(B)。

3、在getBean(B)中同样创建了B的实例对象后会先放入三级缓存中,接着在三级缓存中找到了一个半成品的A实例对象,注入给了属性a,就可以进行下面的流程初始化b并把成品放入单例池中。

4、这时候就走通了流程,把完整的b注入a完成b的依赖注入,接着执行a的初始化,把成品a放入单例池中

这时候看似是已经解决了循环依赖的问题,但是为什么spring要用三级缓存?假设a和b在走完实例化初始化之后要进行功能增强,即要创建代理对象pa、pb,那么应该是把pa、pb作为成品放入一级缓存。这时候流程就会出现问题:b对象中注入a属性的是目标对象而不是增强后的代理对象,这就会导致在调用b使用到其中a的相关功能时是没有增强的对象。

出现这种情况的原因是代理对象创建的时机比较晚,导致放入三级缓存中的a是目标对象,b无法获取增强后的a。解决这种情况的一种做法是把对象增强提前,在实例化后就增强,但是spring还是希望增强发生在在初始化之后,只有发生了循环依赖了才提前创建动态代理。于是就引入了新的缓存:二级缓存,并配合上一个工厂

二级缓存(earlySingletonObjects):

只有出现循环依赖的时候才会用到。用于解决循环依赖下动态代理的问题。对于有循环依赖的情况,AOP在实例化后创建动态代理对象,但是spring还是希望对于没有循环依赖的时候AOP创建的动态代理对象在初始化之后。为了达到这个目的引入了二级缓存,还要配合一个工厂。这个工厂的作用可以理解成一个lamda表达式,用于产生代理对象或者原始对象,检查如果发生了循环依赖,提前创建代理对象返回,如果没有发生AOP增强,还是返回原始对象

 图中的例子:

1、首先单例池没有A对象所以会执行getBean(A)的方法,创建了A的实例对象后,会把工厂对象fa放入三级缓存中,这个工厂对象后面有可能产生代理也有可能返回原始对象。

2、开始a的依赖注入过程,类似的会在创建了B的实例对象后,会把工厂对象fb放入三级缓存中。

3、开始执行b的依赖注入,会去三级缓存(singletonFactories)找有没有半成品的东西,这时候获得的是fa,这时候判断是不是发生了循环依赖,如果发生了循环依赖就换创建代理对象pa(如果没有进行AOP放入的就是原始对象)并注入给b的属性a完成依赖注入,并且把政前后的pa放入二级缓存(earlySingletonObjects)中。

4、把b的结果返回注入给a的属性完成依赖注入,接着完成初始化

5、检查代理有没有创建——去二级缓存中看有没有一个已经创建好的pa,如果有,直接获取并放入一级缓存中,并把二级三级中临时的内容清空

如果循环依赖出现在构造方法中

这时候三级缓存就不能解决循环依赖问题,因为三级缓存的思路是在A创建完之后把工厂对象放进第三级缓存中;而此时A都不能创建成功。

解决构造方法循环依赖的思路

1、@Lazy(代理对象)

先创建一个b的假对象也就是代理对象,完成a的初始化,接着就可以正常的完成b的初始化

加在构造方法的参数上 

2、 使用ObjectFactory(工厂方式)

先创建一个对象工厂起到推迟B创建的作用,就可以完成A的创建并放入一级缓存,B创建可以获得A实例并完成创建,后续A中想要使用B的功能可以通过代理工厂间接获得

3、Provider接口(工厂方式)

与使用ObjectFactory方法类似,起到推迟B创建的作用

 4、@Scope(代理对象)

 让B一开始的就是代理对象,如下面代码,加了@Scope后在创建B的时候就会创建对应的代理对象

 总结:

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值