恒美微站 Logo 恒美微站
  • 首页
  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心
  • 联系我们

Spring 循环依赖是怎么解决的?三级缓存一次讲清楚

  • 首页
  • 资讯中心
  • /
  • Spring 循环依赖是怎么解决的?三级缓存一次讲清楚

相关资讯

沈阳SEO网站关键词优化:新手避坑5大注意事项 2026/9/27 4:33:51
做学校后台网站用什么浏览器完整流程揭秘 2026/9/27 4:33:51
5个网站营销案例复盘:避开备案大坑的注意事项 2026/9/27 4:33:51

最新资讯

我的编程学习之路:目标、方法与时间规划
OptiScaler游戏超分辨率画质增强指南:三步替换任意游戏的超分技术
个人网站建设的国外文献综述报价单里藏了3个坑
【dz-1177】基于单片机的车内防窒息系统
ng-zorro-antd Message 结束事件(onClose):RxJS 驱动下的消息关闭回调与链式操作实战
gsd-core 的 /gsd-capture --seed 一键捕获:告别强制问卷,让灵感流不被中断

今日推荐

从像素到笔画:srt-whiteboard-animation骨架笔迹追踪实现(Zhang-Suen细化+8邻接追踪)
网站建设的英语怎么说?别只背单词,看完这套安全完整流程才敢上线
新手入门看这篇:建设网站加盟避坑指南与SEO实操

本周热门

从像素到笔画:srt-whiteboard-animation骨架笔迹追踪实现(Zhang-Suen细化+8邻接追踪)
网站建设的英语怎么说?别只背单词,看完这套安全完整流程才敢上线
新手入门看这篇:建设网站加盟避坑指南与SEO实操

本月精选

自研推理加速器Redwood:两周内实现PyTorch模型高效部署的实战教程
V4L2摄像头采集实战:从camera_client.rar到出图全流程解析
从“谁发明了钢琴键”到知识问答智能体:RAG与记忆工程实践

Spring 循环依赖是怎么解决的?三级缓存一次讲清楚

发布时间:2026/9/27 4:33:51
Spring 循环依赖是怎么解决的?三级缓存一次讲清楚 Spring 循环依赖是面试里非常常见的问题。很多文章一上来就背“一级缓存、二级缓存、三级缓存”但真正理解它其实只需要抓住一个核心Spring 解决循环依赖的关键是提前暴露一个还没有完全初始化完成的 Bean 引用。一、什么是循环依赖假设有两个 BeanA 依赖 BB 又依赖 AServicepublicclassA{AutowiredprivateBb;}ServicepublicclassB{AutowiredprivateAa;}正常创建过程就会变成创建 A ↓ A 需要 B ↓ 创建 B ↓ B 又需要 A如果 Spring 再重新创建 A就会一直循环下去。那 Spring 是怎么打破这个循环的呢关键在于对象实例化完成之后就已经存在了只是属性还没有注入完成。Spring 创建 Bean 大致可以分为实例化 → 属性填充 → 初始化例如AanewA();执行完这一句A 对象其实已经存在只不过此时A └── b null所以 Spring 可以先把这个“半成品 A”的引用暴露出去让 B 先使用。二、Spring 的三级缓存Spring 为此使用了三级缓存缓存作用singletonObjects一级缓存保存已经初始化完成的单例 BeanearlySingletonObjects二级缓存保存已经提前暴露的 Bean 引用singletonFactories三级缓存保存用于生成提前引用的对象工厂ObjectFactory可以简单记成一级Bean 已经创建完成 二级Bean 还没创建完成但提前引用已经生成 三级提前引用还没生成但我知道怎么生成三、A 和 B 的循环依赖到底怎么解决假设还是A → B B → ASpring 首先创建 AAanewA();A 实例化完成以后并不会马上进入一级缓存因为它还没有完成属性填充和初始化。Spring 会先向三级缓存中放入一个对象工厂三级缓存 A → ObjectFactory这个工厂以后可以用来获取 A 的提前引用。接下来 Spring 给 A 注入 B发现 B 还不存在于是开始创建 B。创建 B 的时候又发现AutowiredprivateAa;B 需要 A。Spring 此时去查找 A一级缓存 → 没有 二级缓存 → 没有 三级缓存 → 找到了 A 的 ObjectFactory于是 Spring 调用这个对象工厂得到 A 的提前引用ObjectFactory ↓ A 的提前引用然后把这个提前引用放入二级缓存二级缓存 A → A 的提前引用B 就可以拿着这个 A 完成自己的依赖注入和初始化。B 创建完成以后进入一级缓存。然后回到 A 的创建流程此时 B 已经有了A └── b → BA 也就可以继续完成初始化最后进入一级缓存。整个过程可以简化成创建 A ↓ 实例化 A ↓ 把 A 的对象工厂放入三级缓存 ↓ A 需要 B ↓ 创建 B ↓ B 需要 A ↓ 从三级缓存获取 A 的提前引用 ↓ 提前引用进入二级缓存 ↓ A 注入 B ↓ B 创建完成 ↓ A 创建完成 ↓ 进入一级缓存四、为什么还需要第三级缓存看到这里可能会有一个疑问既然最终还是要把 A 提前暴露出去为什么不直接把 A 放进二级缓存普通情况下确实可以。问题主要出现在AOP场景。例如ServicepublicclassA{Transactionalpublicvoidtest(){}}因为存在Transactional最终 Spring 暴露出去的 A 很可能不是原始对象而是一个代理对象AProxy ↓ 原始 A如果 Spring 在 A 刚实例化时就直接把原始 A 放到二级缓存B.a → 原始 A但是 A 最终创建完成以后Spring 又产生了代理对象Spring 容器 → AProxy这时候就会变成B 持有原始 A 容器持有AProxy这样 B 调用 A 时就可能绕过 Spring 的 AOP 代理。因此 Spring 没有直接缓存原始 A而是在三级缓存中保存ObjectFactory当真的发生循环依赖需要提前获取 A 时再通过getEarlyBeanReference()获取 A 的提前引用。如果不需要代理可以返回原始 A如果需要 AOP则有机会提前返回代理对象。所以三级缓存的重点并不是“多存了一层 Bean”而是通过ObjectFactory延迟生成 Bean 的提前引用从而让 Spring 有机会决定提前暴露原始对象还是代理对象。第一次生成提前引用以后再把它放到二级缓存后面直接复用即可。五、为什么构造器循环依赖解决不了如果使用构造器注入classA{publicA(Bb){}}classB{publicB(Aa){}}创建 A 时创建 A ↓ 必须先有 B ↓ 创建 B ↓ 必须先有 A这时候最大的问题是AanewA(...);这一句都还没有执行完成。也就是说A 对象本身还不存在自然就没有所谓的“提前引用”可以暴露。因此 Spring 的三级缓存机制主要能处理的是单例 Bean 的 setter / 字段注入循环依赖。而典型的构造器循环依赖无法通过提前暴露引用来解决。六、总结Spring 解决循环依赖可以归纳成一句话利用 Bean 已经实例化、但尚未完成初始化的时间窗口提前暴露 Bean 的引用。三级缓存则可以理解成 Bean 创建过程中的三个状态三级缓存 ObjectFactory “我知道怎么生成提前引用” ↓ 二级缓存 Early Reference “提前引用已经生成” ↓ 一级缓存 Singleton Bean “Bean 已经完整创建”所以面试时与其死记三级缓存的名字不如先记住循环依赖靠“提前暴露引用”解决三级缓存则负责管理这个提前引用从“准备生成”到“已经生成”再到“完整 Bean”的整个过程。另外需要注意Spring 底层具备这套解决部分循环依赖的机制并不代表实际项目应该依赖循环依赖。如果 A、B 长期互相依赖很多时候也说明类之间的职责划分值得重新设计。

关于恒美微站

恒美微站专注于为个体商户、工作室提供极简自助建站服务,让每个人都能轻松拥有专业网站。

快速链接

  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心

服务项目

  • 可视化建站
  • 拖拽编辑
  • 主题定制
  • SEO 优化
  • 网站托管

联系方式

  • 📍 地址:北京市朝阳区建国路 88 号
  • 📞 电话:400-888-8888
  • ✉️ 邮箱:info@hmyw.cn
  • 🕐 时间:周一至周日 9:00-18:00

© 2024 恒美微站 hmyw.cn 版权所有 | 京 ICP 备 12345678 号