# 什么是循环依赖(常问)?

6 min read
Table of Contents

什么是循环依赖(常问)?

循环依赖面试标准答案

【核心定义】

循环依赖是指两个或多个Bean在Spring容器初始化过程中,因相互引用对方的实例而导致的依赖闭环,使容器无法确定Bean的创建顺序。

【关键要点】

  1. 依赖闭环:Bean A依赖Bean B,同时Bean B又依赖Bean A,形成环形依赖链
  2. Spring默认支持单例Bean的循环依赖:通过三级缓存机制解决
  3. 原型Bean不支持循环依赖:每次获取都创建新实例,无法提前暴露引用
  4. 构造器注入不支持循环依赖:构造器调用时Bean尚未创建完成,无法放入缓存

【深度推导/细节】

三级缓存解决机制(逻辑拆解)

三级缓存结构:

  • 一级缓存(singletonObjects):存放完全初始化好的Bean
  • 二级缓存(earlySingletonObjects):存放早期暴露的Bean(已实例化但未属性注入)
  • 三级缓存(singletonFactories):存放Bean工厂,用于生成早期引用

解决流程(以A→B→A为例):

Step 1: 创建Bean A
- 实例化A(调用构造器)
- 将A的ObjectFactory放入三级缓存
- 开始属性注入,发现依赖B
Step 2: 创建Bean B
- 实例化B
- 将B的ObjectFactory放入三级缓存
- 开始属性注入,发现依赖A
Step 3: 获取A的早期引用
- B从三级缓存获取A的ObjectFactory
- ObjectFactory.getObject()返回A的代理/原始对象
- B完成属性注入,放入一级缓存
Step 4: A完成初始化
- A获取到已初始化的B
- A完成属性注入
- A从三级缓存升级到一级缓存

构造器注入为何不支持?

构造器注入发生在实例化阶段,此时:

  1. Bean尚未创建完成,无法放入缓存
  2. 无法提前暴露Bean引用供其他Bean使用
  3. Spring会直接抛出BeanCurrentlyInCreationException

【关联/对比】

Spring不同注入方式的循环依赖支持

注入方式是否支持循环依赖原因
属性注入(setter)✅ 支持Bean实例化后可提前暴露引用
构造器注入❌ 不支持实例化前就需要完整依赖
@Autowired字段注入✅ 支持本质是属性注入的变体

Spring与其它框架对比

  • Spring:通过三级缓存优雅解决单例Bean循环依赖
  • Guice:默认不支持,需要特殊配置(Provider延迟注入)
  • 手动DI:完全依赖程序员控制,容易产生循环依赖

【版本差异】

  • Spring 2.6+:默认禁止了所有循环依赖,需通过spring.main.allow-circular-references=true开启
  • Spring 2.5及之前:默认允许单例Bean的循环依赖
  • 设计演进:新版本更严格,鼓励更清晰的架构设计

『面试官追问』

  1. 三级缓存中为什么要用ObjectFactory而不直接放对象?

    • 支持AOP代理:需要时通过工厂生成代理对象
    • 延迟创建:避免不必要的早期对象创建
  2. 循环依赖在实际项目中是好的设计吗?

    • 虽然Spring能解决,但属于代码”坏味道”
    • 增加耦合度,降低可测试性
    • 建议通过重构(提取公共逻辑、引入第三方Bean)消除
  3. 如何检测和避免循环依赖?

    • IDEA插件可静态检测
    • 启动时Spring会抛出明确异常
    • 定期进行架构评审,保持依赖单向性
  4. 多例Bean为什么不能解决循环依赖?

    • 每次getBean都创建新实例
    • 无法确定哪个实例应该被共享
    • 缓存机制失效,会无限递归创建
  5. 如果A依赖B,B依赖C,C依赖A,三级缓存还能解决吗?

    • ✅ 可以,三级缓存机制支持任意长度的循环链
    • 只要都是单例Bean且非构造器注入即可

回答要点总结:掌握三级缓存的工作机制是理解循环依赖的关键,同时要清楚其使用边界(单例、非构造器注入),并在实际开发中尽量避免这种设计。

My avatar

文章看完了,不打算去别的篇章转转吗?👀 感谢你的耐心阅读!如果你有问题想探讨,或者发现了文中的 Bug,请务必通过页脚的链接联系我


More Posts

# 说说 AQS 吧?

6 min read

AQS 是 Java 并发包中构建锁和同步器的核心框架,通过一个 FIFO 队列 管理线程的排队与唤醒,并利用一个 volatile int 型 state 变量 表示同步状态。

Read

Comments