Spring 循环依赖,别光背三级缓存

举报
yd_241780069 发表于 2026/10/23 22:42:04 2026/10/23
【摘要】 先说结论面试被问循环依赖,很多人张嘴就是“三级缓存”,然后背一遍三个 Map 的名字。但真在项目里遇到 BeanCurrentlyInCreationException 的时候,光知道三级缓存没用,得知道什么情况它能救、什么情况它救不了、救不了的时候该怎么改。我遇到过的循环依赖,最后基本都是靠改代码解决的,不是靠 Spring 兜底。 Spring 能处理哪些限定条件挺死的:单例 + 字段...

先说结论

面试被问循环依赖,很多人张嘴就是“三级缓存”,然后背一遍三个 Map 的名字。但真在项目里遇到 BeanCurrentlyInCreationException 的时候,光知道三级缓存没用,得知道什么情况它能救、什么情况它救不了、救不了的时候该怎么改。

我遇到过的循环依赖,最后基本都是靠改代码解决的,不是靠 Spring 兜底。

Spring 能处理哪些

限定条件挺死的:单例 + 字段注入或 setter 注入。

A 的字段里 @Autowired 了 B,B 的字段里又 @Autowired 了 A,这种 Spring 能绕过去。原理就是 A 实例化完之后、属性还没填的时候,先把 A 的引用提前暴露出去,让 B 能拿到一个“半成品 A”把自己先建完,回头再给 A 填上 B。

构造器注入不行。因为构造器要求“创建 A 的瞬间就把 B 给我”,而 B 又要 A,两边都在等对方完整出生,Spring 连 A 的壳都还没造出来,没东西可以提前暴露,直接报错。Spring Boot 2.6 之后默认还禁止了循环依赖,不手动开 allow-circular-references 的话,字段注入的环也会直接启动失败。

三级缓存到底存了什么

一级 singletonObjects:完整成品,平时 getBean 拿的就是这个。

二级 earlySingletonObjects:半成品,实例化了但属性还没填。从三级缓存里取出来之后放这儿,下次再用就不用再走工厂了。

三级 singletonFactories:存的是 ObjectFactory,一个 Lambda。它的作用不是直接给对象,而是在需要的时候生成早期引用。为什么不能直接存半成品?因为如果这个 Bean 后面要被 AOP 代理,提前暴露的得是代理对象,不是原始对象。工厂模式让“生成代理”这件事延迟到真正被循环依赖引用的时候才发生,没被引用就不生成代理。

所以三级缓存的核心不是“三级”,是延迟决定要不要生成代理。二级缓存做不到这一点,因为放进去的时候代理还没生成。

我实际处理过的几种情况

第一种,构造器注入的环。 两个 Service 互相在构造器里要对方。改法要么把其中一个依赖改成 @Lazy,注入一个代理,真正用的时候才初始化;要么抽第三个小类把公共逻辑挪出去。我一般选后者,@Lazy 只是把问题藏起来了,设计上那个环还在。

第二种,自己注入自己。 之前写过一个类里 @Autowired 了自己,想用代理调方法。Spring 2.6 之后默认不让这么玩了。改成 AopContext.currentProxy() 或者把方法拆到别的 Bean 里。

第三种,@Async 和循环依赖撞上。 有个 Service 带 @Async 方法,又被另一个 Service 注入,启动就报错。原因是异步代理的创建时机和普通 Bean 不一样,三级缓存那套流程对代理对象的处理有坑。最后是把异步方法抽到了独立的组件里,不在有环的 Bean 上挂 @Async。

排查的时候看什么

报错信息一般会直接告诉你哪个 Bean 在创建时撞环了,比如 Requested bean is currently in creation: Is there an unresolvable circular reference?。

日志里搜 Eagerly caching bean 和 using early reference,能看到 Spring 有没有走提前暴露的路径。如果搜不到 using early reference 但报错了,基本就是构造器注入或者代理导致的,缓存流程根本没走到。

小结

循环依赖大部分时候是设计问题,不是配置问题。Spring 的三级缓存是兜底机制,不是鼓励你写环。我现在写新代码的原则很简单:构造器注入,不写环,有环就抽类。真遇到老代码改不动的,@Lazy 临时顶上,但记个 TODO 后面重构。

先记这些。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。