Spring-如何解决循环依赖

什么是循环依赖

循环依赖其实就是循环引用,也就是两个或者两个以上的 Bean 互相持有对方,最终形成闭环。比如A 依赖于B,B依赖于C,C又依赖于A。

注意: 这里不是函数的循环调用,是对象的相互依赖关系。循环调用其实就是一个死循环,除非有终结 条件。

Spring中循环依赖场景有:

  • 构造器的循环依赖(构造器注入)
  • Field 属性的循环依赖(set注入)

其中,构造器的循环依赖问题无法解决,只能拋出 BeanCurrentlyInCreationException 异常,在解决
属性循环依赖时,spring采用的是提前暴露对象的方法。

spring循环依赖处理机制

  • 单例 bean 构造器参数循环依赖(无法解决)
    • 原因:构造参数注入时,对象还没有创建,无法获取对象引用
  • prototype 原型 bean循环依赖(无法解决)
    • 原因:多例模式下,Bean创建后就不归IOC容器管理,无法注入其他对象中
  • 单例bean通过setXxx或者@Autowired进行循环依赖

Spring 的循环依赖的理论依据基于 Java 的引用传递,当获得对象的引用时,对象的属性是可以延 后设置的,但是构造器必须是在获取引用之前。

Spring通过setXxx或者@Autowired方法解决循环依赖;

假设A依赖B,B依赖A:

  1. 新建A,将引用放入三级缓存中
  2. 发现A依赖B:
    1. 从一级缓存查询,没有
    2. 从二级缓存查询,没有
    3. 从三级缓存查询,没有
    4. 新建B,将B引用放入三级缓存中
      a. 发现依赖A
      b. 从一级缓存找,没有
      c. 从二级缓存找,没有
      d. 从三级缓存找到,返回注入
      e. 走B的剩余生命周期处理
    5. B新建完成,存入一级缓存(单例池)中
  3. A注入B完成
  4. A剩余生命周期处理完成,存入一级缓存中
    spring解决循环依赖

如果有三个或者四个循环引用,就一级一级初始化,都是从三级缓存中获取引用注入的;

Spring初始化SingletonBean流程

这里附一张我画的spring初始化Bean的时序图供参考
Spring初始化SingletonBean流程

为什么使用三级缓存,而不是用二级缓存呢?

事实上,如果只是解决循环依赖问题,只有二级缓存就够用了,那为什么spring要使用三级缓存来处理循环依赖问题呢?
这里是因为,spring要确保,预先存储的引用(三级缓存中的),要和一级缓存中的引用一样,如果只是用二级缓存,对于那些,只是注入属性,没有其他额外处理的Spring Bean是没有问题的,它的引用就是一开始构造函数创建后的引用,但是对于那些,最终引用会变化的Spring Bean(比如:AOP生成的代理对象)就有大问题了,会出现,三级缓存中的引用(依赖注入的引用)和最终一级缓存中的引用不一样,导致从三级缓存中注入的属性,会丢失(AOP添加的)功能。
所以,Spring 使用三级缓存解决循环依赖,在从三级缓存注入到属性过程中,会预先(AOP部分处理)生成代理对象,然后注入属性,然后放入二级缓存(其他属性注入时可以直接从二级缓存取用同一个代理对象),最后在对象正常生命周期中会判断此对象是否已经生成过代理对象,如果生成过,则从二级缓存中取出,将前一个对象数据拷贝到代理对象中,再继续spring bean生命周期。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值