首页/新闻资讯/正文详情

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

发布时间:2026/9/27 4:33:22 来源:云帆数科 栏目:资讯中心
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 长期互相依赖很多时候也说明类之间的职责划分值得重新设计。

相关推荐

自己动手,搭一个 AI 资讯聚合平台
自己动手,搭一个 AI 资讯聚合平台

做 AI 相关工作的人,大概都有过这种体验:每天早上先刷一遍 X,再看几个大厂博客,然后去 Hacker News 和 Reddit 翻翻评论区,晚上再补几期 YouTube 访谈。 一圈下来一两个小时没了,真正有用的信息可能就那么几… · 2026/9/27 4:33:16

新手入门避坑:网站开发需要考虑哪些方面才能不被宰
新手入门避坑:网站开发需要考虑哪些方面才能不被宰

新手入门避坑:网站开发需要考虑哪些方面才能不被宰 找建站公司最怕什么?怕报价单上写着“基础版”,签完合同才发现SSL证书要加钱、服务器配置不够要升级、备案指导费另算。这种“低价引流,高价收割”的套路,坑了多少刚想搞网站的老板?我是干这行的,… · 2026/9/27 4:32:52

ob template
ob template

ob template初始化 使用Markdowm语言来撰写笔记 一级标题 item1item2 enumerateenu2 链接到笔记 链接到笔记链接笔记 快捷键: CommandP 打开命令面板快速执行命令 callout 完成笔记初始化 插入表格 ssqq11 printf链接笔记 链接到文字 链接到标题linktitl… · 2026/9/27 4:32:52

MDIO协议详解:从PHY寄存器读写到Linux驱动调试实战
MDIO协议详解:从PHY寄存器读写到Linux驱动调试实战

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

个人知识库搭建指南:从笔记整理到高效复习
个人知识库搭建指南:从笔记整理到高效复习

一、引言 在日常学习和工作中,我们每天都会接触大量信息。如何高效地积累、整理和运用这些知识,是提升学习和工作效率的关键。一个结构清晰的个人知识库,有助于快速检索信息、建立知识之间的联系,让复习和回顾更加高效。下面仅从工… · 2026/9/27 5:08:35

I2C地址扫描实战:100KHz总线测试与Excel记录方法
I2C地址扫描实战:100KHz总线测试与Excel记录方法

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

海思HI3798MV310机顶盒U盘强刷安卓9.0技术指南
海思HI3798MV310机顶盒U盘强刷安卓9.0技术指南

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

ARIMA旅游人数预测实战:从平稳性检验到差分定阶全流程
ARIMA旅游人数预测实战:从平稳性检验到差分定阶全流程

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

鲜花商城网站设计新手入门:3个关键决策避开90%的坑
鲜花商城网站设计新手入门:3个关键决策避开90%的坑

鲜花商城网站设计新手入门:3个关键决策避开90%的坑 不会代码也能搞定网站?这听起来像天方夜谭,但对于想入局鲜花电商的创业者来说,这恰恰是最现实的痛点。很多团队负责人拿着几万块预算,对着满屏的代码报错发呆,或者被外包公司坑得连底裤都不剩。其… · 2026/9/27 5:08:10

MATLAB雷达信号脉冲压缩仿真:LFM线性调频、匹配滤波与距离分辨率实现
MATLAB雷达信号脉冲压缩仿真:LFM线性调频、匹配滤波与距离分辨率实现

简介:这套Matlab仿真工具完整呈现雷达信号脉冲压缩过程,从线性调频(LFM)信号生成、目标回波仿真到匹配滤波压缩处理均有可运行代码支撑,面向电子信息工程、计算机、数学等专业学生,适用于课程设计、期末大作… · 2026/9/27 0:00:01

汕头网站建设制作厂家避坑指南:5大注意事项救急
汕头网站建设制作厂家避坑指南:5大注意事项救急

汕头网站建设制作厂家避坑指南:5大注意事项救急 改个需求建站公司拖一周,这种憋屈事我见得太多了。 很多汕头老板找本地建站团队,签合同前看着方案挺美,一上线就变脸。 今天不聊虚的,直接拆解找 汕头网站建设制作厂家 时的5个核心 注意事项… · 2026/9/27 0:00:01

多模态虚假新闻检测实战:BERT+ResNet双塔与对比学习
多模态虚假新闻检测实战:BERT+ResNet双塔与对比学习

简介:基于PyTorch的多模态虚假新闻检测项目完整代码包,面向自然语言处理与计算机视觉交叉方向的开发者、科研人员及毕业设计选题者,解决社交媒体中文本与图像联合识别虚假新闻的问题。系统以BERT预训练模型提取文本语义特征,以Res… · 2026/9/27 0:00:01

MATLAB雷达信号脉冲压缩仿真:LFM线性调频、匹配滤波与距离分辨率实现
MATLAB雷达信号脉冲压缩仿真:LFM线性调频、匹配滤波与距离分辨率实现

简介:这套Matlab仿真工具完整呈现雷达信号脉冲压缩过程,从线性调频(LFM)信号生成、目标回波仿真到匹配滤波压缩处理均有可运行代码支撑,面向电子信息工程、计算机、数学等专业学生,适用于课程设计、期末大作… · 2026/9/27 0:00:01

汕头网站建设制作厂家避坑指南:5大注意事项救急
汕头网站建设制作厂家避坑指南:5大注意事项救急

汕头网站建设制作厂家避坑指南:5大注意事项救急 改个需求建站公司拖一周,这种憋屈事我见得太多了。 很多汕头老板找本地建站团队,签合同前看着方案挺美,一上线就变脸。 今天不聊虚的,直接拆解找 汕头网站建设制作厂家 时的5个核心 注意事项… · 2026/9/27 0:00:01

多模态虚假新闻检测实战:BERT+ResNet双塔与对比学习
多模态虚假新闻检测实战:BERT+ResNet双塔与对比学习

简介:基于PyTorch的多模态虚假新闻检测项目完整代码包,面向自然语言处理与计算机视觉交叉方向的开发者、科研人员及毕业设计选题者,解决社交媒体中文本与图像联合识别虚假新闻的问题。系统以BERT预训练模型提取文本语义特征,以Res… · 2026/9/27 0:00:01

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码