Spring 解决bean的循环依赖

Spring循环依赖-博客园

1. 什么是循环依赖

2. 循环依赖能引发什么问题

循环依赖可能引发以下问题:

  • 初始化顺序不确定:循环依赖导致无法确定哪个对象应该先被创建和初始化,从而造成初始化顺序的混乱。这可能导致错误的结果或意外的行为。
  • 死锁和无限递归:当循环依赖中的对象在创建过程中相互等待对方完成创建时,可能会导致死锁或无限递归的情况发生。这将使程序无法继续执行,最终导致系统崩溃或进入无限循环。
  • 难以理解和维护:循环依赖增加了代码的复杂性和耦合度,使得代码难以理解和维护。在一个循环依赖链中,修改一个类可能会影响到其他所有相关的类,增加了代码的脆弱性。
  • 可测试性下降:循环依赖使得各个模块或类之间的责任不清晰,难以进行单独的单元测试或模块拆解。这给软件的测试和调试带来困难,降低了可测试性和可维护性。

3. Spring是怎么处理循环依赖的

为避免循环依赖带来的问题,应尽量避免在设计和实现中出现循环依赖关系。重新考虑系统的架构,采用合适的设计模式、解耦方式和依赖注入机制,可以帮助解决或避免循环依赖带来的问题。

据此我们得到以下结论:

spring容器中原型(Prototype)的场景是不支持循环依赖的,抛出BeanCurrentlyInCreationException异常

其实,Spring容器中单例(singleton)的场景是支持循环依赖的,Spring使用了三级缓存和提前曝光(early exposure)的机制来处理循环依赖问题

  • 第一级缓存(也叫单例池)singletonObjects:存放已经经历了完整生命周期的Bean对象
  • 第二级缓存: earlySingletonObjects,存放早期暴露出来的Bean对象,Bean的生命周期未结束(属性还未填充完整)
  • 第三级缓存: Map<String, ObiectFactory<?>> singletonFactories,存放可以生成Bean的工厂

所谓的三级缓存其实就是spring容器内部用来解决循环依赖问题的三个map

Spring实例Bean的本质

  • 实例化
    • 堆内存中申请一块内存空间
    • 租赁好房子,自己的家具东西还没有搬家进去
  • 初始化属性填充
    • 完成属性的各种赋值
    • 装修、家电家具进场

3大Map和四大方法,总体相关对象
在这里插入图片描述

1.getSingleton:从容器里面获得单例的bean
2.doCreateBean: 没有就创建bean
3.populateBean: 创建完了以后,要填充属性
4.addSingleton: 填充完了以后,再添加到容器进行使用

第一层singletonObjects存放的是已经初始化好了的Bean,
第二层earlySingletonObjects存放的是实例化了,但是未初始化的Bean,
第三层singletonFactories存放的是FactoryBean。假如A类实现了FactoryBean,那么依赖注入的时候不是A类,而是A类产生的Bean
A/B两对象在三级缓存中的迁移说明

  1. A创建过程中需要B,于是A将自己放到三级缓存里面,去实例化B
  2. B实例化的时候发现需要A,于是B先查一级缓存,没有,再查二级缓存,还是没有,再查三级缓存,找到了A然后把三级缓存里面的这个A放到二级缓存里面,并删除三级缓存里面的A
  3. B顺利初始化完毕,将自己放到一级缓存里面(此时B里面的A依然是创建中状态) 然后回来接着创建A,此时B已经创建结束,直接从一级缓存里面拿到B,然后完成创建,并将A自己放到一级缓存里面。

https://blog.csdn.net/weixin_41883161/article/details/139706538

Spring框架通过三级缓存(三级缓存机制)来解决大多数情况下的循环依赖问题。三级缓存机制包括以下三个层次:

  • 单例池(singletonObjects):用于存放完全初始化好的单例Bean。
  • 早期曝光对象池(earlySingletonObjects):用于存放部分初始化完成的单例Bean,通常是通过提前暴露Bean引用来解决循环依赖。
  • 三级缓存(singletonFactories):用于存放Bean工厂,以便在需要时创建Bean实例。
> 4.1 三级缓存机制详解
> 4.1.1 单例池(singletonObjects) 单例池是一个Map,用于存放完全初始化好的单例Bean。当Spring容器创建一个Bean时,会首先检查单例池中是否已经存在该Bean,如果存在则直接返回,否则继续创建。
> 
> 4.1.2 早期曝光对象池(earlySingletonObjects) 早期曝光对象池是一个Map,用于存放部分初始化完成的单例Bean。当Spring容器检测到循环依赖时,会将部分初始化完成的Bean提前放入该池中,以便其他Bean能够引用。
> 
> 4.1.3 三级缓存(singletonFactories) 三级缓存是一个Map,用于存放Bean工厂。Bean工厂是一个用于创建Bean实例的对象,当需要创建Bean实例时,Spring容器会从三级缓存中获取相应的Bean工厂,并通过它来创建Bean实例。

4.2 三级缓存的工作流程

Spring容器创建Bean A,首先检查单例池中是否存在Bean A。
如果单例池中不存在Bean A,则检查早期曝光对象池中是否存在Bean A。
如果早期曝光对象池中也不存在Bean A,则从三级缓存中获取Bean A的工厂,并通过该工厂创建Bean A的实例。
创建Bean A实例的过程中,发现Bean A依赖于Bean B,因此开始创建Bean B。
创建Bean B的过程中,发现Bean B依赖于Bean A,此时检测到循环依赖。
将Bean A的实例放入早期曝光对象池中,以便Bean B可以引用。
继续完成Bean B的创建,并将其放入单例池中。
返回Bean B的实例,继续完成Bean A的创建,并将其放入单例池中。
通过上述流程,Spring容器可以成功处理大多数情况下的循环依赖。

4. Spring怎样注入才能不产生循环依赖问题

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值