学会Spring的循环依赖,轻松避免构造机无法创建的问题!

文摘   2024-12-09 22:00   江苏  

大家好,今天跟大家聊聊Spring框架里一个让人头疼的问题——循环依赖。这就像两只小狗互相追着尾巴咬,没完没了。循环依赖指的是两个或多个Bean之间互相依赖,形成一个闭环。简单来说,就是A依赖B,B依赖C,C又依赖A,这就套娃了,形成了循环依赖。Spring循环依赖会导致Bean无法正常创建,程序启动失败或者抛出异常。所以,我们要尽量避免这种情况。

循环依赖的常见原因

构造器循环依赖

用构造器注入Bean的时候,如果两个Bean互相依赖,就会形成构造器循环依赖。就像两个小孩子,一个说“你先给我玩具,我才给你糖”,另一个说“你先给我糖,我才给你玩具”,僵持不下。

ounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(line@Componentpublic class A {    private B b;    public A(B b) {        this.b = b;    }}@Componentpublic class B {    private A a;    public B(A a) {        this.a = a;    }}

这段代码中,A依赖B,B也依赖A,形成了循环依赖。Spring容器启动时,要先创建A,但是创建A需要B,创建B又需要A,这就死循环了。

属性循环依赖

使用属性注入Bean时,如果两个Bean互相依赖,也会形成属性循环依赖。

ounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(line@Componentpublic class A {    @Autowired    private B b;}@Componentpublic class B {    @Autowired    private A a;}

Spring在创建A时要注入B,注入B时又要注入A,又循环了。

Spring如何解决循环依赖

Spring创建Bean的过程

Spring创建Bean的过程就像工厂生产产品一样,有几个步骤:

  1. 加载Bean定义: Spring容器启动后,会加载Bean的配置信息,就像工厂拿到产品的设计图纸。
  2. 创建Bean实例: 根据配置信息,通过反射创建Bean实例,就像工厂根据图纸生产产品。
  3. 属性装配: 给Bean注入依赖的属性,就像给产品组装零件。循环依赖的解决主要就在这一步。
  4. 初始化Bean: 执行Bean的初始化方法,就像产品出厂前的最后检测。

Spring的三级缓存机制

Spring能解决的循环依赖

注意:只有单例的Bean才存在循环依赖问题,Spring才能解决。原型(Prototype)类型的Bean,Spring会直接抛出异常。

Spring不支持构造器注入的循环依赖。如果A和B循环依赖,一个是构造器注入,一个是setter注入,会怎么样呢?

  • A构造器注入B,B setter注入A:可以解决。
  • B构造器注入A,A setter注入A:不行。

简单来说,setter注入的循环依赖Spring可以解决,构造器注入不行。构造器和setter混合的情况,得看具体情况。

三级缓存机制

Spring用三级缓存来解决循环依赖,就像工厂用了三个仓库:

  1. 一级缓存(singletonObjects): 存放完整的Bean实例,就像成品仓库。
  2. 二级缓存(earlySingletonObjects): 存放实例化但未初始化的Bean实例,就像半成品仓库。
  3. 三级缓存(singletonFactories): 存放Bean的创建工厂,就像零件仓库。

三级缓存解决循环依赖的过程

假设A和B循环依赖:

  1. 创建A实例,将A的工厂放入三级缓存,就像把A的零件放入零件仓库。
  2. A注入属性时,发现依赖B,就去创建B。
  3. B注入属性时,发现依赖A,就从缓存中找A。先从一级缓存找,没有;再从二级缓存找,也没有;最后从三级缓存找到A的工厂,用工厂创建A的早期实例,并将A的早期实例放入二级缓存,同时删除三级缓存中的A工厂。
  4. B创建完成,放入一级缓存。
  5. A继续注入属性,从一级缓存中找到B,A创建完成,放入一级缓存。

温馨提示

  • 循环依赖尽量避免,可以通过重新设计代码结构来解决。
  • Spring只能解决单例Bean的setter注入循环依赖。

总结

循环依赖是Spring中一个比较复杂的问题,理解Spring的三级缓存机制是解决循环依赖的关键。希望这篇文章能帮助大家更好地理解Spring循环依赖。

夜半探案
每日一案,一案一法,一起学习生活中的法律知识。
 最新文章