spring如何解决循环依赖

Spring 解决循环依赖核心依靠:三级缓存 + 提前暴露早期半成品 Bean ,仅支持单例、构造器注入以外的场景。


一、什么是循环依赖

两个或多个 Bean 互相依赖:

java 复制代码
@Component
public class A {
  @Autowired
  private B b;
}

@Component
public class B {
  @Autowired
  private A a;
}

二、核心原理:三级缓存

Spring 容器维护三个 Map(三级缓存):

  1. singletonObjects(一级缓存)

    • 存放完全初始化好的单例 Bean
  2. earlySingletonObjects(二级缓存)

    • 存放实例化完成,但未填充属性、未初始化的早期 Bean
  3. singletonFactories(三级缓存)

    • 存放创建早期 Bean 的ObjectFactory,用于获取早期引用

三、解决流程(A ↔ B)

  1. 创建 A:实例化 A,将 A 的 ObjectFactory 放入三级缓存,暴露早期引用
  2. 填充 A 属性:发现依赖 B,去创建 B
  3. 创建 B:实例化 B,将 B 的 ObjectFactory 放入三级缓存
  4. 填充 B 属性:发现依赖 A,从缓存获取 A:
    • 先查一级缓存(无)
    • 查二级缓存(无)
    • 查三级缓存,拿到 A 的 ObjectFactory,获取早期 A 对象,放入二级缓存,并删除三级缓存
  5. B 完成属性填充 + 初始化,加入一级缓存
  6. 回到 A,拿到完整 B,完成 A 初始化,加入一级缓存

四、哪些情况无法解决

Spring 无法解决以下循环依赖,启动报错:

  1. 构造器注入循环依赖

    java 复制代码
    @Component
    public class A {
      // 构造器注入 B
      public A(B b) {}
    }
    
    @Component
    public class B {
      // 构造器注入 A
      public B(A a) {}
    }

    实例化时就互相依赖,无法提前暴露。

  2. 多例(prototype)Bean

    多例不进入缓存,每次创建新实例,无法缓存提前引用。

  3. 使用 @DependsOn 显式构成循环依赖。


五、无法解决时的方案

  1. 改用 setter / 字段注入(而非构造器)

  2. 使用 @Lazy 懒加载:

    java 复制代码
    @Component
    public class A {
      @Autowired
      @Lazy
      private B b;
    }
  3. 拆分子功能,解除循环依赖

  4. 使用 ApplicationContextAware、InitializingBean 手动获取依赖


六、总结

  • 默认支持:单例 + setter / 字段注入循环依赖
  • 核心手段:三级缓存提前暴露半成品 Bean
  • 不支持 :构造器注入、多例、@DependsOn 循环
  • 优化 :优先用 @Lazy 或重构代码解除循环
相关推荐
Dwzun14 分钟前
基于SpringBoot3+Vue3的油莎豆种植管理系统
java·前端·vue.js·spring boot·后端·python·计算机网络
小码哥_常6 小时前
一套自己部署的 IM,客户端是同一份 Flutter
前端·后端
学长毕设9 小时前
基于SpringBoot的实验室资产管理系统
java·spring boot·后端
zzu123zsw10 小时前
从臃肿的模板调用到声明式契约:深入 UniHttp 动态代理与多渠道生命周期扩展
java·spring boot·microsoft·unihttp
源码集结号11 小时前
云诊所门诊系统源码,基于SpringBoot+Vue.js架构,支持多租户设计与Docker容器化部署
java·spring boot·源码·医院信息系统·门诊系统·诊所系统
努力的小雨11 小时前
给 AI 眼镜开发应用有多难?我从零做了一个陪练,还上架了
后端
论文复现现场11 小时前
WRF、FEA 能用 L40S 加速吗?求解器检查、断点恢复与长期运行
java·数据库·算法·wrf·有限元分析·gpu计算·断点续算
啃瓜子的松鼠12 小时前
泛微OAe9-后端二开异步调用
java
秋天的怀念_David12 小时前
第二十一夜 · 数字黑洞与西西弗斯的惩罚
java·程序人生·算法
excel12 小时前
双 Token 与前端使用 Crypto 公私钥鉴权换 Token,哪种方案更好?
前端·后端