Spring 循环依赖:三级缓存背得再熟,不如看这 3 个线上坑
循环依赖是 Spring 项目里一不留神就会碰到的问题尤其在多人开发的项目里两个人各写各的 Service一个要调 A一个要调 B合代码时谁也没注意互相引了——启动直接红字BeanCurrentlyInCreationException: Error creating bean with name a: Requested bean is currently in creation: Is there an unresolvable circular reference?我最常碰到的还不是简单的 A 依赖 B、B 依赖 A 两两互指而是链式的A 注入 BB 注入 CC 又注入 A——三个类串成一个环启动必崩。下面从原理到解法讲透附能跑的代码。一、先 30 秒理解什么是循环依赖就是 A 依赖 BB 依赖 CC 又依赖 A对象创建时形成闭环ServicepublicclassA{privatefinalBb;// 依赖 B}ServicepublicclassB{privatefinalCc;// 依赖 C}ServicepublicclassC{privatefinalAa;// 又依赖回 A成环}注意这不是函数递归调用那种是死循环是对象创建时的相互依赖。环上有几个 bean 都叫循环依赖链式三个以上比两两互指更常见。二、补充知识Bean 的三种注入方式要理解循环依赖为什么难解先看 Spring 注入 Bean 的三种方式注入方式写法优点缺点构造器注入推荐构造器参数 /RequiredArgsConstructor依赖明确、final 不可变、对象创建即完整官方推荐循环依赖时启动即崩实例化阶段就卡住字段注入Autowired private B b;代码最少依赖藏在字段里难测试、难发现循环官方不推荐setter 注入Autowired setB(B b)可选依赖友好创建后依赖可变不如构造器稳推荐默认用构造器注入——依赖一目了然、不可变、好测试。只有遇到循环依赖时才用Lazy破环坑 1 讲别为了省事全程字段注入。三、3 分钟理论Spring 靠三级缓存解决Spring 创建单例 Bean 分三步实例化createBeanInstance调构造器new 出对象填充属性populateBean注入依赖初始化initializeBeanAOP 代理、init 方法等循环依赖卡在第 1、2 步之间。Spring 的处理是提前暴露对象构造完、还没填充属性时就先放进缓存让别人拿。它有三层缓存缓存作用singletonFactories三级存对象工厂提前暴露的关键earlySingletonObjects二级存提前暴露的早期对象singletonObjects一级存初始化完成的成品 Bean流程是这样的A 实例化完把自己塞进三级缓存 → 填充属性发现要 B → 去创建 B → B 实例化完发现要 A → 一级没有、二级没有、三级有B 从三级缓存拿到 A 的引用 → B 顺利初始化完进一级缓存 → A 拿到 B也完成初始化。关键结论三级缓存的加入前提是对象已经调过构造器了——所以setter/字段注入能解决循环依赖构造器注入解决不了后者直接抛BeanCurrentlyInCreationException。理论就这么多面试够用了。下面是真正折磨人的 3 个实战坑。坑 1构造器注入的链式循环启动直接崩就是最开始的报错。现在流行构造器注入最规范但多人开发时链式循环一来启动必崩ServicepublicclassA{privatefinalBb;publicA(Bb){this.bb;}}ServicepublicclassB{privatefinalCc;publicB(Cc){this.cc;}}ServicepublicclassC{privatefinalAa;// 依赖回 A成环publicC(Aa){this.aa;}}启动即报BeanCurrentlyInCreationException。解法 1Lazy推荐改动最小——打破环上的任意一条边ServicepublicclassC{privatefinalAa;publicC(LazyAa){this.aa;}// 延迟解析环被打破}Lazy让 Spring 先注入一个代理对象真正调用时才去拿目标 Bean。解法 2换成字段注入不推荐用它掩盖问题ServicepublicclassC{AutowiredprivateAa;// 字段注入走 setter 流程能过}字段注入本质是填充属性阶段注入走了三级缓存路径所以能解。但正如第二节说的字段注入是官方不推荐的方式——用它破环属于能跑但丑根治看坑 3。坑 2循环依赖 事务/切面方法静默失效这个坑最阴启动不报错业务悄悄出错。场景A 有Transactional方法B 依赖 A。因为循环依赖B 在早期阶段拿到的 A不是最终完成 AOP 代理的成品而是提前暴露的早期对象ServicepublicclassA{AutowiredprivateBb;TransactionalpublicvoiddoSth(){}}ServicepublicclassB{AutowiredprivateAa;// 拿到的可能是早期对象publicvoidcall(){a.doSth();// 事务可能没生效}}结果就是doSth()里要么事务边界不对要么依赖切面的逻辑如Async、自定义 AOP表现诡异排查起来极其费时——因为你单测、正常调用都对只有走这条循环链路时才坏。解法还是Lazy让 B 真正调用 A 时才解析保证拿到的是完整代理ServicepublicclassB{AutowiredLazyprivateAa;// 真正调用时才解析拿完整代理}教训看到循环依赖 事务/切面组合别能跑就行必须把代理问题一起验证否则上线就是事故。坑 3循环依赖往往是设计坏味道这是我最想强调的一点。两个或多个Service 互相注入八成是职责边界划错了。最常见的案例A 其实只需要 B 的某个动作却直接注入了 B 的整个能力。正确的重构方向按优先级抽公共依赖把互相调用的那部分逻辑下沉到独立的 Service两边都依赖它环就断了单向调用审视业务很多互相调用其实有一方是多余的事件解耦A 做完发 Spring Event / 用 MQB 监听处理彻底不互相持有// 重构后A 只依赖抽出来的 CommonService不再反向ServicepublicclassA{privatefinalCommonServicecommonService;// 单向}ServicepublicclassCommonService{// 谁都不回依赖 A环断了}Lazy是止痛药重构才是根治。如果项目里循环依赖超过 2 处先别急着Lazy想想是不是设计该调了。总结场景结论构造器注入循环依赖启动即崩用Lazy或改字段注入setter/字段注入循环依赖三级缓存能解但慎用循环依赖 事务/切面可能静默失效必须验证代理多处循环依赖重构边界抽公共 Service 或事件解耦你项目里遇到过最诡异的循环依赖是啥是启动崩还是业务悄悄错评论区聊聊。相关阅读Spring 事务失效的 11 个真实场景——和这篇的坑 2 正好能串起来都是代码看着对、运行出问题的典型。本文首发于公众号「今天写BUG了么」关注看更多

相关新闻

黄金微针按次报价怎样核对包含项和变更差价

黄金微针按次报价怎样核对包含项和变更差价

黄金微针写着“按次收费”,并不自动说明一次包括哪些内容。到了比较报价或调整方案时,真正影响支出的,是服务范围怎样变化、原付款有多少可以用于新方案,以及哪些款项仍在单独处理中。先统一口径,再算差额,…

2026/9/24 4:50:28 阅读更多 →
国产安全MCU LKT6830C开发实战:硬件加密与防篡改设计

国产安全MCU LKT6830C开发实战:硬件加密与防篡改设计

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/24 4:50:28 阅读更多 →
Storm 安全加固:Kerberos 认证、ACL 权限与多租户隔离

Storm 安全加固:Kerberos 认证、ACL 权限与多租户隔离

Storm 安全加固:Kerberos 认证、ACL 权限与多租户隔离Apache Storm 作为分布式实时计算框架,广泛应用于实时数据处理场景。随着企业级应用的需求增长,Storm 平台的安全性也日益重要。本文将详细介绍 Storm 安全加固的三大核心机制&#xff1a…

2026/9/24 4:50:28 阅读更多 →

最新新闻

Mac录屏全攻略:自带工具与OBS等专业方案选型指南

Mac录屏全攻略:自带工具与OBS等专业方案选型指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/24 6:21:22 阅读更多 →
VC6.0程序在Win11上闪退?三步修复兼容性实战指南

VC6.0程序在Win11上闪退?三步修复兼容性实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/24 6:21:22 阅读更多 →
STM32G4 ADC硬件过采样与软件滤波实战:从配置到代码落地

STM32G4 ADC硬件过采样与软件滤波实战:从配置到代码落地

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/24 6:21:22 阅读更多 →
VCS后仿真SDF警告处理:编译选项实战指南

VCS后仿真SDF警告处理:编译选项实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/24 6:21:22 阅读更多 →
球杆平衡系统:从PID到LQR的控制算法实战指南

球杆平衡系统:从PID到LQR的控制算法实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/24 6:21:22 阅读更多 →
Presto Release 0.161 技术详解:ORDER BY 语义变更、EXCEPT 正确性修复与连接器增强

Presto Release 0.161 技术详解:ORDER BY 语义变更、EXCEPT 正确性修复与连接器增强

大数据数据库后端 【免费下载链接】presto The official home of the Presto distributed SQL query engine for big data 项目地址: https://gitcode.com/gh_mirrors/pre/presto 点击查看 免费下载 导读 本文基于 Presto 官方发布说明 release-0.161.rst&#xf…

2026/9/24 6:20:22 阅读更多 →

日新闻

基于YOLOv8的渔船作业监控系统:从环境搭建到边缘部署全流程

基于YOLOv8的渔船作业监控系统:从环境搭建到边缘部署全流程

简介:这是一套面向计算机、人工智能、自动化等专业学生与教师的毕业设计级项目资源,围绕YOLOv8实现渔船作业监控系统,可用于毕设、课程设计、大作业或项目立项演示。压缩包共97个文件,约24.21MB,以70个Python源码文件为…

2026/9/24 0:00:19 阅读更多 →
单细胞注释实战:基于Scanpy的标记基因与参考映射流程解析

单细胞注释实战:基于Scanpy的标记基因与参考映射流程解析

简介:一份基于单细胞RNA测序数据的细胞类型注释算法研究Python毕业设计源码,针对计算机相关专业正在做毕设或需要项目实战的学习者,可用于课程设计与期末大作业。项目代码完整、经导师指导评审通过,可直接运行,覆盖数据…

2026/9/24 0:00:19 阅读更多 →
C#源生成器实战:用增量生成器替代反射,告别AOT崩溃

C#源生成器实战:用增量生成器替代反射,告别AOT崩溃

第一次在项目里被反射卡住,是在一个老旧的WinForms模块里:几十个类依赖PropertyChanged通知,运行时反射读属性、发通知,每次启动慢半拍不说,一上.NET Native/AOT裁剪模式几乎全面崩盘。后来我把这段逻辑全部改成C#源生…

2026/9/24 0:00:19 阅读更多 →

周新闻

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

直接铺开项目本身吧。这几个月我一直在折腾一件事:用Flutter给OpenHarmony做一款游戏集合类的App,说白了就是把若干小游戏塞进一个壳里,用统一入口分发。这个方向本身不算新鲜,真正让我花了不少心思的,是首页那堆游戏卡…

2026/9/23 4:55:02 阅读更多 →
Word表格编号全攻略:从列表编号到题注交叉引用

Word表格编号全攻略:从列表编号到题注交叉引用

写Word文档,最让人头疼的往往是那些“看起来不起眼”的小问题。比如表格编号这事:今天在表后面多加了两个空白行,明天给客户交稿前发现整个章节的编号全部错位,光是挨个改序号就能耗掉大半个下午。我前阵子帮人整理一份上百页的技…

2026/9/23 4:49:06 阅读更多 →
从第一个站到第二个站:独立开发者的静态网站选型与落地实践

从第一个站到第二个站:独立开发者的静态网站选型与落地实践

1. 项目概述1.1 核心需求解析做独立开发者这几年,说实话,第一个网站上线的那天晚上我兴奋得没睡着。但等它跑了半年,流量惨淡、功能臃肿、代码自己都懒得看第二遍之后,我才慢慢琢磨明白一个道理:第一个网站是练手&…

2026/9/23 9:53:41 阅读更多 →

月新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

2026/9/23 9:53:40 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…

2026/9/23 9:53:40 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步分类:[工程技术]细分主题:Docker 容器化技术与镜像安全管理:核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用(包含 Web 接口、后台…

2026/9/23 9:53:40 阅读更多 →