1049 字
约 3 分钟
5
S07-J02 · 循环依赖与三级缓存

适用读者:被 BeanCurrentlyInCreationException 或"三级缓存"绕晕的人。
前置:S07-B02。
实测环境:Spring Boot 3.3.6 / JDK 17(2026-11-05)。
覆盖旧文:974(79) 973(77) = 156 点。
覆盖扩展:E-S07-013(三级缓存各存什么)、E-S07-014(构造器注入为何无解)。
上一篇:S07-J01 · 事务:传播机制与失效的六种原因
下一篇:S07-J03 · Spring Event。


1. 一句话结论

三级缓存是"提前暴露半成品 Bean"的机制,只为解决 setter/字段注入的循环依赖;构造器注入的循环无法解决——Spring Boot 2.6+ 默认直接禁止。

实测(构造器互相注入):

APPLICATION FAILED TO START
The dependencies of some of the beans in the application context form a cycle
Relying upon circular references is discouraged and they are prohibited by default.
... it may be possible to break the cycle automatically by setting
    spring.main.allow-circular-references to true

异常链里还有一句关键的:Requested bean is currently in creation: Is there an unresolvable circular reference?


2. 三级缓存各存什么(E-S07-013)

缓存 内容 作用
一级 singletonObjects 完整 Bean 正常获取
二级 earlySingletonObjects 提前暴露的原始 Bean(未完成注入) 避免重复创建
三级 singletonFactories ObjectFactory(可生成代理) AOP 代理在此生成

为什么需要三级而不是两级:

只用两级 问题
提前暴露原始对象 如果该 Bean 需要 AOP 代理,其他 Bean 拿到的是原始对象,不是代理 → AOP 失效

三级缓存的工厂可以"按需生成代理"——这是它存在的真正理由,不是"多缓存一份"。


3. 解决流程(setter 注入)

创建 A → 实例化 A(未注入)→ 把 A 的 ObjectFactory 放入三级缓存
        → 注入 B → 创建 B → 注入 A → 从三级缓存拿到 A(或代理)
        → B 完成 → 注入回 A → A 完成

关键顺序:实例化在前、注入在后——所以 setter/字段注入能"先给个空壳"。


4. 为什么构造器注入无解(E-S07-014)

构造器必须在实例化时就有依赖,没有"半成品"阶段可暴露 → 无法提前暴露 → 循环无法打破。

注入方式 循环依赖
字段/setter ✅ 可解(三级缓存)
构造器 ❌ 无解(Boot 2.6+ 直接报错)

这也是一句重要论断的依据:"能靠三级缓存救的循环依赖,本身就是设计问题"——官方从 2.6 起默认禁止(spring.main.allow-circular-references=true 可临时放开)。


5. 正确的处理方式

方式 说明
重构依赖方向 抽出第三个 Bean 承载公共逻辑(首选)
@Lazy 注入代理,延迟到用时才解析
事件/回调 用 Event(J03)解耦反向依赖
放开开关 临时方案,会掩盖设计问题

6. 五个误区

误区 真相
"Spring 能解决所有循环依赖" 构造器注入不行
"三级缓存只是为了性能" 核心是提前暴露代理
"两级缓存就够" 有 AOP 时会拿到原始对象
"循环依赖是正常现象" Boot 2.6+ 默认禁止
"@Lazy 是最佳方案" 掩盖问题,优先重构

7. 自检

  1. 三级缓存各存什么?为什么第三级必须有?
  2. 为什么构造器注入的循环无解?
  3. Boot 2.6+ 对循环依赖的默认行为?
  4. 四种处理方式里,哪个是首选?
  5. "两级缓存出问题"的具体场景是什么?

8. 下一篇

S07-J03 · Spring Event:事件发布订阅

循环依赖讲完,下一篇给"解耦反向依赖"的正规工具:事件机制(实测同步在发布线程、异步在 task-1)。


本篇属于系列《S07 · Spring 框架》。旧文来源:A-974/973;扩展点 E-S07-013/014。启动报错为 Spring Boot 3.3.6 实测。

S07-J02 · 循环依赖与三级缓存
https://www.clxhxhhr.top/posts/5275/
作者
clxstart
发布于
2024-12-31
许可协议
CC BY-NC-SA 4.0
评论
0 条
还没有评论,先写一条吧。