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放入单例池。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Fire king

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

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

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

打赏作者

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

抵扣说明:

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

余额充值