Spring循环依赖原理与三级缓存机制解析
1. Spring循环依赖问题解析从现象到本质Spring框架作为Java企业级开发的基石循环依赖问题几乎每个开发者都会遇到。当Bean A依赖Bean B同时Bean B又依赖Bean A时就形成了典型的循环依赖场景。Spring通过三级缓存机制巧妙地解决了这个问题但理解其原理需要深入框架内部。我在实际项目中最常遇到的循环依赖场景是服务层相互调用。比如订单服务(OrderService)需要调用库存服务(StockService)检查库存而库存服务又需要调用订单服务查询历史订单数据。如果不加处理直接注入Spring启动时就会抛出BeanCurrentlyInCreationException异常。关键提示Spring只能解决单例作用域Bean通过setter/field注入方式的循环依赖。原型(prototype)作用域的Bean或构造器注入方式都会导致创建失败。1.1 三级缓存工作机制详解Spring的三级缓存机制是解决循环依赖的核心具体分为singletonObjects一级缓存存储完全初始化好的Bean实例earlySingletonObjects二级缓存存储提前暴露的原始Bean引用singletonFactories三级缓存存储Bean工厂对象用于生成原始Bean引用当创建Bean A时Spring的执行流程如下实例化Bean A调用构造函数将Bean A的ObjectFactory放入三级缓存填充Bean A的属性时发现需要Bean B开始创建Bean B填充Bean B的属性时发现需要Bean A从三级缓存获取Bean A的早期引用Bean B完成初始化Bean A获得Bean B引用完成初始化// Spring源码中的关键方法 protected Object getSingleton(String beanName, boolean allowEarlyReference) { Object singletonObject this.singletonObjects.get(beanName); if (singletonObject null isSingletonCurrentlyInCreation(beanName)) { synchronized (this.singletonObjects) { singletonObject this.earlySingletonObjects.get(beanName); if (singletonObject null allowEarlyReference) { ObjectFactory? singletonFactory this.singletonFactories.get(beanName); if (singletonFactory ! null) { singletonObject singletonFactory.getObject(); this.earlySingletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName); } } } } return singletonObject; }1.2 循环依赖的典型解决方案除了依赖Spring的自动处理我们还可以主动设计避免循环依赖接口抽离将相互依赖的部分提取到接口层应用事件使用ApplicationEvent实现松耦合通信方法调用替代属性注入通过ApplicationContext.getBean()延迟获取Lazy注解延迟初始化依赖对象Service public class OrderService { Lazy // 关键注解 Autowired private StockService stockService; //... }2. 三级缓存背后的设计哲学2.1 为什么需要三级而非一级缓存三级缓存的设计体现了Spring框架的精妙之处。如果只有一级缓存在Bean未完全初始化前就放入缓存其他Bean可能获取到不完整的实例。三级缓存通过ObjectFactory实现了延迟处理可以应对AOP代理等复杂场景。2.2 循环依赖的处理边界Spring并非能解决所有循环依赖以下情况仍会失败构造器注入方式的循环依赖prototype作用域的Bean循环依赖Async注解方法引起的循环依赖Transactional注解在循环依赖场景下的特殊表现实战经验在Spring Boot 2.6版本中默认禁止了循环依赖需要在配置中显式开启spring.main.allow-circular-referencestrue3. 面试深度问题剖析3.1 高频面试题解析为什么三级缓存能解决循环依赖通过提前暴露对象引用打破循环链条ObjectFactory的延迟处理能力应对代理场景分层缓存确保不同阶段获取合适的对象引用Spring如何检测循环依赖使用ThreadLocal记录当前正在创建的Bean在创建Bean前检查依赖链是否形成环通过isPrototypeCurrentlyInCreation方法判断Lazy注解的工作原理创建代理对象而非真实实例首次方法调用时触发实际初始化适用于构造器注入的循环依赖场景3.2 源码级问题准备面试官可能会要求手写简化版的三级缓存实现public class SimpleBeanContainer { private final MapString, Object singletonObjects new ConcurrentHashMap(); private final MapString, Object earlySingletonObjects new ConcurrentHashMap(); private final MapString, ObjectFactory? singletonFactories new HashMap(); public Object getBean(String name) { Object bean singletonObjects.get(name); if (bean null) { bean earlySingletonObjects.get(name); if (bean null) { ObjectFactory? factory singletonFactories.get(name); if (factory ! null) { bean factory.getObject(); earlySingletonObjects.put(name, bean); singletonFactories.remove(name); } } } return bean; } public void registerBean(String name, ObjectFactory? factory) { if (!singletonObjects.containsKey(name)) { singletonFactories.put(name, factory); } } }4. 生产环境中的最佳实践4.1 循环依赖的预防策略架构设计层面遵循单一职责原则拆分过大服务引入中间层打破直接依赖使用领域事件解耦业务逻辑代码规范层面避免双向依赖保持单向引用优先使用构造器注入明确依赖关系对必要循环依赖添加文档说明4.2 性能优化考量三级缓存虽然解决了循环依赖但也带来额外开销缓存查找的多层判断逻辑额外的对象包装和转换并发场景下的同步控制在性能敏感场景下可以通过以下方式优化使用DependsOn明确初始化顺序将频繁交互的Bean合并采用静态方法代替实例方法5. 复杂场景下的特殊处理5.1 AOP代理与循环依赖当循环依赖的Bean需要AOP代理时Spring的处理更为复杂。代理对象的创建时机直接影响循环依赖能否解决JDK动态代理基于接口在Bean初始化后创建CGLIB代理基于子类在Bean初始化前创建Spring通过SmartInstantiationAwareBeanPostProcessor确定最佳代理时机确保循环依赖正确处理。5.2 多级循环依赖场景对于更复杂的A→B→C→A多级循环依赖三级缓存机制同样适用。Spring会逐级创建Bean直到发现循环通过缓存回溯获取早期引用自下而上完成各Bean初始化// 多级循环依赖示例 Service public class ServiceA { Autowired private ServiceB serviceB; } Service public class ServiceB { Autowired private ServiceC serviceC; } Service public class ServiceC { Autowired private ServiceA serviceA; }6. 版本变迁与兼容性不同Spring版本对循环依赖的处理有所差异版本范围特性变化兼容性建议4.3以前构造器注入循环依赖部分支持避免使用构造器注入4.3-5.2改进的代理处理逻辑检查AOP配置5.3优化缓存查找性能关注初始化速度Boot 2.6默认禁止循环依赖显式配置允许在微服务架构下跨服务的循环依赖更应该通过以下方式解决引入消息队列异步处理使用Feign客户端熔断机制设计最终一致性方案7. 调试技巧与问题诊断当遇到循环依赖问题时可以采用以下诊断方法异常堆栈分析BeanCurrentlyInCreationException包含循环链查看Requested bean is currently in creation提示调试模式设置logging.level.org.springframework.beansDEBUG可视化工具Spring Boot Actuator的/beans端点IDEA的Spring Beans依赖图简化复现SpringBootTest public class CircularTest { Test void testContextLoads() { // 启动失败即存在无法解决的循环依赖 } }对于复杂的循环依赖问题我的经验是先使用Lazy临时解决再通过重构逐步消除。记录一个典型处理过程发现启动时报循环依赖错误在关键依赖点添加Lazy使应用启动分析依赖关系图找出设计问题通过接口抽离或事件驱动重构代码逐步移除Lazy注解添加单元测试防止回归循环依赖就像代码中的技术债越早处理成本越低。在项目初期就应当建立架构评审机制预防不合理的依赖关系产生。

相关新闻

阴阳师自动化脚本终极解放方案:如何实现全自动游戏体验

阴阳师自动化脚本终极解放方案:如何实现全自动游戏体验

阴阳师自动化脚本终极解放方案:如何实现全自动游戏体验 【免费下载链接】OnmyojiAutoScript Onmyoji Auto Script | 阴阳师脚本 项目地址: https://gitcode.com/gh_mirrors/on/OnmyojiAutoScript 你是否厌倦了每天重复点击屏幕完成阴阳师的各种日常任务&…

2026/10/14 17:36:47 阅读更多 →
Java线程调度中yield()的原理与实战优化

Java线程调度中yield()的原理与实战优化

1. 线程调度的本质与yield的定位在Java并发编程中,线程调度器负责决定哪个线程可以获得CPU时间片。默认采用的时间片轮转调度算法中,每个线程会获得固定的执行时长(通常10-100ms),当时间片用尽或线程主动放弃时发生上下…

2026/10/13 21:52:54 阅读更多 →
写作压力小了!盘点2026年备受推崇的AI论文网站

写作压力小了!盘点2026年备受推崇的AI论文网站

一天写完毕业论文在2026年已不再是天方夜谭。最新实测数据显示,2026年AI论文网站正以惊人效率重塑学术写作,覆盖选题构思、文献综述、内容生成、格式排版等核心场景,真正实现高效搞定论文。 一、全流程王者:一站式搞定论文全链路&…

2026/10/14 18:20:16 阅读更多 →

最新新闻

ZeroTermux 内置命令手册精讲:zcat 命令——不真正解压也能查看 gzip 压缩包内容

ZeroTermux 内置命令手册精讲:zcat 命令——不真正解压也能查看 gzip 压缩包内容

移动开发开发工具 【免费下载链接】ZeroTermux 项目地址: https://gitcode.com/GitHub_Trending/ze/ZeroTermux 点击查看 免费下载 本篇围绕 ZeroTermux 内置 Linux 命令手册(app/src/main/assets/cmd/zcat.md)展开,系统讲解 zca…

2026/10/15 18:12:24 阅读更多 →
基于CAN总线的温度采集与控制系统设计:抗干扰与主动报警实践

基于CAN总线的温度采集与控制系统设计:抗干扰与主动报警实践

简介:基于CAN总线的温度采集与控制系统完整工程包,面向嵌入式、STM32及CAN总线通信学习者,解决环境温度实时采集与上位机监控的实践需求。该工程以STM32为控制核心,结合DS18B20温度传感器与CAN收发节点,并配套上位机串…

2026/10/15 18:12:24 阅读更多 →
DeepSpec 实战指南:投机解码草稿模型的数据准备、训练与评估全流程

DeepSpec 实战指南:投机解码草稿模型的数据准备、训练与评估全流程

人工智能大模型预训练模型优化模型评测 【免费下载链接】DeepSpec DeepSpec: a full-stack codebase for training and evaluating speculative decoding algorithms 项目地址: https://gitcode.com/gh_mirrors/de/DeepSpec 点击查看 免费下载 DeepSpec 是一个面向…

2026/10/15 18:12:24 阅读更多 →
PCRE 8.45 正则库实战:编译、API 调用与性能优化指南

PCRE 8.45 正则库实战:编译、API 调用与性能优化指南

简介:pcre-8.45.tar.gz 是 PCRE(Perl Compatible Regular Expressions)库 8.45 版的官方源代码压缩包,面向需要在 CentOS 等 Linux 服务器环境中自行编译部署正则表达式引擎的开发者与运维人员。PCRE 以 C 语言编写,支…

2026/10/15 18:12:24 阅读更多 →
南大通用GBase 8a数据库压缩算法新老版本对照

南大通用GBase 8a数据库压缩算法新老版本对照

南大通用GBase 8a数据库(gbase database)旧算法配置参数中,可以控制使用算法的种类,而具体到某一算法,但只能采用默认的压缩级别,缺乏灵活性,也限制了算法在不同场景下发挥出最大效能。同时现在…

2026/10/15 18:12:24 阅读更多 →
IPMItool源码解析:从接口抽象到BMC诊断命令扩展

IPMItool源码解析:从接口抽象到BMC诊断命令扩展

简介:IPMItool 源码包是一份面向服务器运维工程师、系统管理员及BMC嵌入式开发者的开源工具源码,用于通过IPMI协议对BMC进行远程监控、传感器数据读取与电源管理。资源共171个文件,压缩包仅752KB,以50个头文件和49个C源文件为核心…

2026/10/15 18:11:23 阅读更多 →

日新闻

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

1. 为什么选择容器化方式部署 LoRa 网络服务LoRa 物联网项目落地时,网络服务器是整个链路的核心枢纽。它负责接收网关上报的数据包、完成去重与解密、再把有效载荷分发给上层应用。早期大家习惯把网络服务器直接装在物理机上,依赖一堆系统库和特定版本的…

2026/10/15 0:00:46 阅读更多 →
共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

1. 共享推理服务里,扩缩容到底难在哪做过大模型推理服务部署的人,大概都经历过这样的场景:一个团队共用一套推理集群,A 业务跑的是 7B 的小模型,B 业务跑的是 72B 的大模型,C 业务白天流量高、晚上几乎归零…

2026/10/15 0:00:46 阅读更多 →
Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

1. 引子:一次内核恐慌引发的追查先从一个真实场景说起。某次我给一台老旧的嵌入式设备折腾系统裁减,为了把启动时间压进两秒以内,我手贱地把一串自认为“没用”的 systemd 服务给 disable 了,其中就包括systemd-remount-fs.servic…

2026/10/15 0:00:46 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/15 16:18:54 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/14 16:30:29 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/15 13:48:15 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

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

2026/10/15 0:14:36 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

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

2026/10/14 22:45:47 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

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

2026/10/15 9:49:14 阅读更多 →