Spring是如何解决循环依赖的?

Posted Fire king

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring是如何解决循环依赖的?相关的知识,希望对你有一定的参考价值。

实例化对象的基本步骤:

  1. 创建bean原始对象

  2. 设置target class用于代理对象的创建

  3. 允许循环依赖->三级缓存存入原始对象,并未执行lambda表达式 //上三步用于B

  4. 填充属性

循环依赖现象例子:

  1. 实例化A

  2. 填充B->单例池->找不到->创建B
    2.1.实例化B
    2.2.填充A->单例池->找不到->创建A

二级缓存解决循环依赖:

  1. 实例化A 存入Map

  2. 填充B->单例池->找不到->创建B
    2.1.实例化B 存入Map earlysingletonObject
    2.2.填充A->单例池->找不到->Map //一定可以找到
    2.3.填充其他属性
    2.4.AOP
    2.5.将B放入单例池

  3. 填充其他属性

  4. AOP

  5. 将A放入单例池

三级缓存引入的原因:

为了解决AOP,AOP一定会产生一个代理对象-》矛盾:有原始对象A和代理对象A,放到单例池中的应该是哪个

  1. 实例化A 。判断是否循环依赖

    (三个条件:是否单例,是否允许循环依赖,当前对象是否正在创建中)->条件一定符合:创建三级缓存,一个lambda表达式,但未执行

  2. 填充B->单例池->找不到->创建B
    2.1.实例化B 存入Map
    2.2.填充A->单例池->找不到->二级缓存->找不到->三级缓存,执行lambda表达式,将原始A对象存入earlyProxyReferences中,为A对象执行第4步做准备,同时生成代理对象,将代理对象存入二级缓存,返回代理对象。
    2.3.填充其他属性
    2.4.AOP
    2.5.将B放入单例池

  3. 填充其他属性

  4. AOP

    代理此时AOP的逻辑会判断earlyProxyReferences中有没有原始对象 bean,有就证明代理对象题前暴露,就直接返回原始对象,没有就会创建并返回代理对象。这样做就是为了不能重复创建多个相同的代理对象。

  5. 将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是如何解决循环依赖的?的主要内容,如果未能解决你的问题,请参考以下文章

源码分析:Spring如何解决单例Bean的循环依赖?

spring为啥要使用三级缓存解决循环依赖

Spring到底是如何解决循环依赖的?

彻底理解Spring如何解决循环依赖

面试题:如何解决Spring 的循环依赖问题

Spring 是如何解决循环依赖的