Spring IoC - 如何解决循环依赖

前面说到对象的创建,那么在创建的过程中Spring是怎么又是如何解决循环依赖的呢。前面提到有个三级缓存。就是利用这个来解决循环依赖。打个比方说实例化A的时候,先将A创建(早期对象)放入一个池子中。这个时候虽然属性没有赋值,但是容器已经能认识这个是A对象,只是属性全是null而已。在populateBean方法中对属性赋值的时候,发现A依赖了B,那么就先去创建B了,又走一遍bean的创建过程(创建B)。同样也会把B的早期对象放入缓存中。当B又走到populateBean方法的时候,发现依赖了A,好吧,我们又去创建A呗,但是这个时候去创建A,发现我们在缓存能找到A(早期对象)了。就可以把B的A属性赋值了,这个时候B就初始化完成了。现在回到A调用的populateBean方法中。返回的就是B对象了,对A的B属性进行赋值就可以了。流程如下:

image

这就是Spring IOC如何解决循环依赖的原理,但是IOC无法解决两种循环依赖,一种是非单例对象的,因为非单例对象不会放入缓存的。每次都是需要创建。二是通过构造器注入,也无法解决。从上面的流程可以看出,调用构造器创建实例是在createBeanInstance方法,而解决循环依赖是在populateBean这个方法中,执行顺序也决定了无法解决该种循环依赖。对于这种,如果喜欢使用lombok的@RequiredArgsConstructor注解的小伙伴就需要注意了,这个注解会生成一个带所有属性的构造方法。通过idea直接点开对应的class文件就可以看不看这个构造方法了。

问题:解决循环依赖一定要三级缓存嘛?

关键代码位置:org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#addSingletonFactory

首先我们的一级缓存是单例缓存池(singletonObjects),二级缓存是早期对象(earlySingletonObjects),三级缓存是一个包裹对象ObjectFactory(registeredSingletons),通过getObject获取到早期对象。

从上面的流程来看,实际上二级缓存已经可以解决循环依赖了,那么为什么Spring还要包裹出来一个三级缓存呢?

Spring Ioc(3)---bean的实例化中的源码分析我们可以看到调用三级缓存中的getObject方法实际上是调用的org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#getEarlyBeanReference这个方法。

image

那么我就看看这个方法里面干了些什么事情:

执行SmartInstantiationAwareBeanPostProcessor这个类型处理器的getEarlyBeanReference方法。

image

断点调试,发现Spring原生中实现了这个接口的类org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter,实际上什么事情都没有干,直接返回了,

image

但是如果是AOP要切的对象,会在这里生成代理对象返回,AOP流程:Spring aop(1)--- 寻找切面和代理对象执行流程源码分析

image

org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#getEarlyBeanReference。

所以说第三级缓存其实是为了解决代理对象之间的循环依赖。

 

来源:https://www.cnblogs.com/nijunyang/p/12365048.html

 

自己理解的案例:

一级缓存:单例池,成型的SpringBean的缓存

二级缓存:早期池

三级缓存:singletonFactorys:为了提前暴露自己

Class A {

private B b;

}

Class B {

private A a;

}

当初始化A的时候,A在对象实例化之后立马放入三级缓存(提前暴露自己);当A创建的过程时发现依赖B,则进行B的初始化,同样B在对象实例化之后立马放入三级缓存(提前暴露自己),同时创建过程中发现依赖A,那么去三级缓存使用未成形的Bean A,并将A升级放到二级缓存中,然后B继续初始化并完成初始化,将B放入到一级缓存,并将二级和三级缓存中移除自己,然后代码返回到A初始化过程,这时候将完整的B复制给A的属性上,A继续初始化并完成初始化,将A放入到一级缓存,并将二级和三级缓存中移除自己。这就是解决循环依赖的过程!

指的注意的是: 只有是单例并且是不通过构造器注入的依赖Bean才能这么解决循环依赖问题。

 

这就是Spring IOC如何解决循环依赖的原理,但是IOC无法解决两种循环依赖:

一种是非单例对象的,因为非单例对象不会放入缓存的。每次都是需要创建。

二是通过构造器注入,也无法解决。从上面的流程可以看出,调用构造器创建实例是在createBeanInstance方法,而解决循环依赖是在populateBean这个方法中,执行顺序也决定了无法解决该种循环依赖。

 

image.png

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

松鼠喵

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值