Spring 循环依赖的三级缓存

在Spring Bean 的生命周期中,里面有一步就是填充属性。而填充属性之前会判 属性对象是否被当前对象循环依赖,当发现属性对象被循环依赖的时候会进行aop(被命中)并且生成属性对象的代理对象(未命中目标对象)。

循环依赖是如何形成的
在这里插入图片描述
当 对象UserA 实例化完成,进行填充属性UserB 的时候 ,先去单例池里面去获取 UserB 对象,初次没有获取到,开始实例化UserB ,当UserB 实例化完成,进行填充属性UserA 的时候,先去单例池里面去获取 UserA 对象,初次没有获取到,再去实例化UserA ,并进行填充属性UserB,此时造成死循环。

单例池 存放的是经过完成生命周期的对象即最终的产出的SpringBean。

如何解决循环依赖
我们先回顾一下Spring Bean生命周期的大致步骤 ,如下图
在这里插入图片描述

如何解决呢
1.添加一个缓存MapA,如图所示
在这里插入图片描述
当实例化UserA 的时候 先将实例化好的UserA 对象放到 MapA 中,再来填充它的UserB 属性,单例池里面没有,去实例化UserB ,并将实例化好的UserB 对象放到 MapA 中,再来填充 UserB对象的 UserA属性,单例池里面没有,去缓存 MapA 里面获取并且获取到了,直接给属性UserA 赋值,UserB 创建完成,将UserB放入单例池,再将 UserA对象 的 UserB 属性赋值,再完成UserA 的创建,最后将UserA 放入单例池。

但是根据上面的生命周期而言,循环依赖赋值的属性对象值并不是最终的那个Spring Bean ,只是一个实例对象。

就是说这个缓存里面放的并不是最终的对象(虽然内存里面都是同一份地址,但是这个对象可能被AOP表达式命中的对象呢)

那么如果 MapA 里面存放的是实例化或者是被代理的对象呢?
那次此时 放入 MapA 缓存之前还需要判断 这个对象是否需要进行代理。如果需要进行代理则存放代理对象,如果不需要代理,则存放原始目标对象即当前的实例化对象。

如下图
在这里插入图片描述
这样最终的放入单例池或者属性赋值的对象就是一个完整的Spring Bean了。

这样一来又有一个问题。

如何判断这个实例化的对象需要提前进行代理?

(因为正常流程生成AOP对象是在初始化之后干的事)

发生循环依赖的对象(被aop 表达式命中的对象产出循环依赖也会提前)
那又如何判断当前正在被创建的对象产生了循环依赖呢

如下图所示
在这里插入图片描述
当正在创建UserA对象的时候,将当前正在被创建的UserA对象放入 setA 集合中。当UserA被实例化出来,填充UserB属性,去setA 集合判断有没有,再创建UserB 对象,再填充UserB 对象的UserA 属性时,去serA 集合判断有没有,如果有即代表UserA被循环依赖了。

如下图所示
在这里插入图片描述
那么这样是不是就解决了循环依赖的问题呢?

我们刚才的场景是这样的
在这里插入图片描述
那如果又依赖了UserC 呢
在这里插入图片描述
当 UserA 和 UserB 发生了循环依赖问题,我们生成了一个UserA 的代理对象。那如果此时 UserA 和 UserC 也发生了循环依赖。当UserA 对象里面的UserB 属性填充完成,再继续填充UserC 的时候是不是也会生成另外一个新的代理对象了。那最终存入单例池的那个最终的User的代理对象就不是同一份呢。
当然这个问题其实在在缓存里面判断一下UserA的代理对象是否存在即可解决问题。

但是 Spring 在解决循环依赖的时候使用了三层缓存。我们当前已知的缓存只有两个 第一个单例池 和 第二个 MapA。那第三个怎么来的呢?

继续看这个图
在这里插入图片描述
从实例化UserA开始到下面的UserB里面填充UserA属性,再生成UserA的代理对象,那生成UserA代理对象的目标对象怎么得到?源码中并没有把实例化好的原始UserA对象当作一个参数去导出传啊,此时加入第三个集合 MapB 就是 形成了Spring 解决循环依赖的三个集合了。

在这里插入图片描述

Spring 解决循环依赖的三级缓存
1.singletonObjects --单例池
2.earlySingletonObjects – MapA
3.singletonFactories – MapB

单例池存放的都是最终的经过完整生命周期的Spring Bean 对象
MapA 存放的是目标对象或者代理对象
MapB 存放的是实例化的原始对象即目标对象

singletonsCurrentlyInCreation setA

源码跟踪地址:循环依赖源码流程

共勉

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值