Spring是如何解决循环依赖的?
Posted Fire king
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring是如何解决循环依赖的?相关的知识,希望对你有一定的参考价值。
实例化对象的基本步骤:
-
创建bean原始对象
-
设置target class用于代理对象的创建
-
允许循环依赖->三级缓存存入原始对象,并未执行lambda表达式 //上三步用于B
-
填充属性
循环依赖现象例子:
-
实例化A
-
填充B->单例池->找不到->创建B
2.1.实例化B
2.2.填充A->单例池->找不到->创建A
二级缓存解决循环依赖:
-
实例化A 存入Map
-
填充B->单例池->找不到->创建B
2.1.实例化B 存入Map earlysingletonObject
2.2.填充A->单例池->找不到->Map //一定可以找到
2.3.填充其他属性
2.4.AOP
2.5.将B放入单例池 -
填充其他属性
-
AOP
-
将A放入单例池
三级缓存引入的原因:
为了解决AOP,AOP一定会产生一个代理对象-》矛盾:有原始对象A和代理对象A,放到单例池中的应该是哪个
-
实例化A 。判断是否循环依赖
(三个条件:是否单例,是否允许循环依赖,当前对象是否正在创建中)->条件一定符合:创建三级缓存,一个lambda表达式,但未执行
-
填充B->单例池->找不到->创建B
2.1.实例化B 存入Map
2.2.填充A->单例池->找不到->二级缓存->找不到->三级缓存,执行lambda表达式,将原始A对象存入earlyProxyReferences中,为A对象执行第4步做准备,同时生成代理对象,将代理对象存入二级缓存,返回代理对象。
2.3.填充其他属性
2.4.AOP
2.5.将B放入单例池 -
填充其他属性
-
AOP
代理此时AOP的逻辑会判断earlyProxyReferences中有没有原始对象 bean,有就证明代理对象题前暴露,就直接返回原始对象,没有就会创建并返回代理对象。这样做就是为了不能重复创建多个相同的代理对象。
-
将A放入单例池
解决:可以通过判断是否AOP决定将哪个放入单例池,矛盾根源并不是这个,而是:B对象填充了原始对象A,而我们应该要的是填充代理对象A。
疑惑:
-
为什么2.2中生成的代理对象不直接放进单例池(一级缓存)?
因为单例池放的对象都是完整的,此时生成的A的代理对象中包含原始对象的引用,而原始对象的属性并未填充,不算完整对象。
-
2.中寻找B对象逻辑有点不对劲?
一般都是从一级缓存找,找不到就二级缓存,再找不到就是三级缓存。因为此时只有A->B,判断不出是否存在循环依赖,而设计上就将这个体现为“是否创建中”,不符合就直接从返回一级缓存取出来的对象,故直接返回了null,正常创建B并放入单例池。只有B->A才能A是创建中为真。
-
3.代码逻辑是三种中的哪一种?
纯第二种不存在,第三种完全包含并取代第二种,第一种是不存在循环依赖的情况下退化而成的:例如A->B
A->创建三级缓存->一级缓存找不到B,返回null->由于B->A不成立,A的三级缓存不会执行,不会提前创建代理对象,也不会保存原始对象->创建B对象->填充B->B有AOP就AOP->B放入单例池->填充A->A有AOP就AOP->A放入单例池。
以上是关于Spring是如何解决循环依赖的?的主要内容,如果未能解决你的问题,请参考以下文章