Spring系列:Spring如何解决循环依赖

本文介绍了Spring框架中循环依赖的概念,如何通过依赖注入和三级缓存机制解决此类问题,以及在构造器注入与setter注入中的应用。
摘要由CSDN通过智能技术生成

❤ 作者主页:欢迎来到我的技术博客😎
❀ 个人介绍:大家好,本人热衷于Java后端开发,欢迎来交流学习哦!( ̄▽ ̄)~*
🍊 如果文章对您有帮助,记得关注点赞收藏评论⭐️⭐️⭐️
📣 您的支持将是我创作的动力,让我们一起加油进步吧!!!🎉🎉

1、什么是循环依赖

在Spring框架中,循环依赖指的是两个或多个Bean之间相互依赖的情况,形成了依赖环路(Circular Dependency)。换句话说,这些Bean之间存在着相互引用,导致无法正确地创建和初始化这些 Bean。

通俗来讲,就是 Bean A 依赖 Bean B,而 Bean B 又依赖于 Bean A,或者是 Bean C 依赖自己本身,如下图所示:

在这里插入图片描述


2、Spring实例Bean的本质

在Spring中,当实例化一个Bean时,会按照依赖关系递归地实例化其所依赖的所有Bean,直到某个Bean不再依赖其他Bean或其依赖已经被实例化过。

具体来说,当实例化 Bean A 时,如果 Bean A 有依赖另一个 Bean B,Spring会先实例化 Bean B,并将其注入到 Bean A 中。而如果 Bean B 又依赖其他 Bean C,那么Spring会先实例化 Bean C,并将其注入到 Bean B 中,以此类推,直到找到一个 Bean 没有依赖其他Bean为止。


3、Spring可以解决哪些情况下的循环依赖

Spring解决循环依赖是由前置条件的:

  • 出现循环依赖的Bean必须要是单例(singleton),如果依赖prototype则完全不会有此需求
  • 依赖注入的方式不能全是构造器注入的方式
依赖情况依赖注入方式是否解决
AB循环依赖AB均采用构造器注入
AB循环依赖AB均采用setter方式注入
AB循环依赖AB均采用属性自动注入
AB循环依赖A中注入的B为setter注入,B中注入的A为构造器注入
AB循环依赖B中注入的A为setter注入,A中注入的B为构造器注入

注意: 第四种可以而第五种不可以的原因是 Spring 在创建 Bean 时默认会根据自然排序进行创建,所以 A 会先于 B 进行创建。


4、Spring怎么解决循环依赖

Spring通过 三级缓存 解决循环依赖:

  1. 一级缓存 Map<String,Object> singletonObjects:存放完全初始化好的Bean集合
  2. 二级缓存 Map<String,Object> earlySingletonObjects:存放创建好但没有初始化属性的Bean集合
  3. 三级缓存 Map<String,ObjectFactory<?>> singletonFactories:存放正在被创建的Bean的集合

当A、B两个类发生循环依赖时,我们看一下Spring是怎么解决循环依赖的:

  1. 创建A实例,实例化的时候把A对象工厂放入三级缓存,表示A开始实例化了,虽然这个对象还不完整,但是先曝出来让大家知道
    在这里插入图片描述
  2. A注入属性时,发现依赖于B,此时B还没有创建出来,所以先去实例化B。
  3. 同样的,B在注入属性时发现依赖于A,它就会从缓存里找A对象。以此从一级缓存到三级缓存去查询A,从三级缓存通过对象工厂拿到A,发现A虽然不太完善,但是却存在,于是把A放入二级缓存,同时删除三级缓存中的A。此时,B已经实例化并且初始化完成,把B放入到一级缓存

在这里插入图片描述
4. 接着A继续属性赋值,顺利从一级缓存中拿到实例化且初始化完成的B对象。此时,A对象也创建完成,删除二级缓存中的A,同时把A放入到一级缓存
5. 最后,一级缓存中保存实例化、初始化完成的A、B对象,Spring也顺利解决了循环依赖的问题。
在这里插入图片描述
注意:

因此,我们就知道为什么Spring能解决setter注入的循环依赖了,因为实例化和属性赋值是分开的,所以里面有操作的空间。如果都是构造器注入的话,那么都得在实例化这一步完成注入,所以自然是无法支持了。


5、为什么解决Spring循环依赖需要三级缓存,而二级缓存却不行

Spring框架解决循环依赖的过程中确实使用了三级缓存。这是因为在单纯的二级缓存情况下,可能会出现无法解决的循环依赖问题。

二级缓存仅仅可以解决同一个Bean在同一个解析过程中的循环依赖,但如果存在多个解析过程,二级缓存就无法满足需求。所以,Spring引入了三级缓存,以便更好地管理和解决多个Bean之间的循环依赖问题。

三级缓存的引入使得Spring可以在不同解析阶段间共享缓存,有效地解决了复杂的循环依赖情况,确保了Bean的正确初始化。


6、总结

  • 处理循环依赖有多种方式。首先考虑是否能够通过重新设计依赖来避免循环依赖。
  • 如果确实不可避免需要循环依赖,那么通过上面提到的方式来处理。优先建议使用setter注入来解决。

 
非常感谢您阅读到这里,如果这篇文章对您有帮助,希望能留下您的点赞👍 关注💖 分享👥 留言💬thanks!!!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Java技术一点通

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

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

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

打赏作者

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

抵扣说明:

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

余额充值