spring三级缓存解决循环依赖问题

发布时间:2026/9/29 2:31:31

spring三级缓存解决循环依赖问题 spring三级缓存(解决循环依赖)什么是循环依赖多个 Bean 互相依赖对方形成闭环。//A依赖BComponentpublicclassA{AutowiredprivateBb;}//B依赖AComponentpublicclassB{AutowiredprivateAa;}A 需要 B 才能完成创建B 又需要 A 才能完成创建 → 循环依赖分类字段/setter注入(单例): spring可以通过三级缓存解决构造器注入: Spring 无法解决直接抛出BeanCurrentlyInCreationExceptionprototype 作用域 Bean完全不支持循环依赖为什么构造器注入的循环依赖不能解决构造器注入调用构造方法的时候就必须把依赖全部传入。ComponentpublicclassA{publicA(Bb){}}创建 A 的时候调用构造函数就必须拿到 B创建 B 又要 A。对象还没有实例化出来没有机会把半成品对象放入三级缓存直接卡死。setter / 字段注入流程先 new 对象实例化→ 对象已经存在放入三级缓存 → 再填充属性。 对象已经实例完成才有机会暴露半成品。三级缓存仅针对单例、setter / 字段注入构造器注入无法解决循环依赖。三级缓存都存于DefaultSingletonBeanRegistry类中。// 一级缓存完整实例化完成的单例BeanprivatefinalMapString,ObjectsingletonObjects;// 二级缓存实例化完成但还未填充属性、未初始化的原始Bean对象early‑referenceprivatefinalMapString,ObjectearlySingletonObjects;// 三级缓存Bean工厂对象用于生成Bean的早期代理对象privatefinalMapString,ObjectFactory?singletonFactories;各类缓存作用一级缓存 singletonObjects存放完全创建完毕实例化 属性填充 初始化的单例 Bean。作用Bean 最终成品仓库。获取逻辑获取 Bean 优先查一级缓存。如果在这里拿到对象直接返回流程结束。循环依赖过程中Bean 还没完成属性填充不会放入一级缓存。三级缓存 singletonFactories存放Object Factorylambda / 工厂回调不是 Bean 对象本身。对象实例化完成new 出来对象属性还没填充就把一个 lambda 工厂放入三级缓存。addSingletonFactory(beanName,()-getEarlyBeanReference(beanName,mbd,bean));核心作用用于在需要的时候生成该 Bean 的 AOP 代理对象。如果这个 Bean 需要 AOP 代理工厂会返回代理对象不需要代理直接返回原始对象。注意:不会立刻执行工厂方法只有别的 Bean 需要引用它的时候才执行工厂拿到对象。为什么要用工厂而不是直接放对象 不是所有 Bean 都发生循环依赖大部分 Bean 不需要提前代理。延迟执行代理逻辑节省性能。二级缓存 earlySingletonObjects存放已经调用三级工厂生成出来的早期对象原始对象 / 代理对象实例化完成但属性未填充作用缓存三级工厂执行后的结果避免重复执行代理逻辑。当从三级缓存执行 Object Factory 拿到对象之后就把对象放到二级缓存同时删除三级缓存中的 factory。后续其他 Bean 再依赖这个 Bean直接从二级缓存取不再重复执行代理。二级缓存解决同一个 Bean 被多个 Bean 循环引用时不要重复创建代理。完整三级缓存处理循环依赖流程A 依赖 BB 依赖 A1.创建 A实例化 new A 对象属性还没填充。把 A 的代理工厂放入三级缓存 singletonFactories。**2.**填充 A 的属性发现需要 B开始创建 B。3.创建 B实例化 new B 对象把 B 的代理工厂放入三级缓存。**4.**填充 B 的属性发现需要 A。尝试获取 A。​ 1.一级缓存无 A​ 2.查询三级缓存执行 A 的 ObjectFactory得到 A 的早期对象原始 / 代理​ 3.将 A 放入二级缓存 earlySingletonObjects删除三级缓存中的 A 工厂。5.B 拿到 A 早期对象完成 B 属性填充、初始化B 完成放入一级缓存 singletonObjects。6. ** A 拿到完整的 B 对象完成 A 属性填充、初始化A 完成放入一级缓存**删除二、三级缓存中 A 的记录。缓存存储内容核心作用一级 singleton Objects完整就绪的单例 Bean存放最终成品 Bean优先获取完整 Bean二级 earlySingleton Objects实例化完成、未填充属性的 Bean (原始 / 代理)保存三级工厂执行后的对象避免重复代理三级 singleton FactoriesObject Factory 对象工厂 (lambda)延迟生成 Bean 的早期代理对象只有被别人依赖才执行关键面试点1.如果没有 AOP 代理理论上二级缓存就可以解决循环依赖引入三级缓存是为了延迟 AOP 代理创建。 2.构造器注入无法解决对象还没有实例化根本没有机会放入三级缓存。 3.仅支持 scopesingletonprototype 多实例不支持循环依赖. 4.Bean 创建完成后二、三级缓存会清理只有一级缓存保留对象。Spring 必须靠三级缓存才能解决循环依赖吗结论不是。如果项目完全没有 AOP 代理只用【一级缓存 二级缓存】就可以解决循环依赖。 Spring 设置三级缓存singletonFactories是专门为处理 AOP 代理场景不是解决普通对象循环依赖的必需品。场景1: Bean 不需要 AOP 代理无 Transactional、无切面此时三级缓存里面的工厂执行直接返回原始对象。逻辑等价实例化完直接把原始对象放入二级缓存不需要工厂。这种场景两级缓存就足够解决循环依赖。场景2: Bean 需要 AOP 代理如加了 Transactional这是三级缓存存在的真正理由。Bean 刚实例化完成属性还没填充正常 AOP 代理是在初始化之后才创建。 但是发生循环依赖时别的 Bean 要提前引用这个 Bean此时必须提前生成代理对象。三级缓存存放的是回调工厂addSingletonFactory(beanName, () - getEarlyBeanReference(beanName, mbd, bean));getEarlyBeanReference()如果需要代理就提前生成代理不需要代理返回原对象。关键点工厂不会立刻执行。只有其他 Bean 需要引用这个 Bean触发循环依赖才执行工厂生成代理对象。如果没有发生循环依赖工厂永远不执行代理还是走正常流程初始化之后创建避免无谓提前代理提升性能。如果去掉三级缓存只保留一级 二级会发生什么方案实例化 Bean 之后直接生成代理对象放入二级缓存。功能上可以解决循环依赖但是性能有代价所有单例 Bean实例化完成之后不管会不会发生循环依赖全部提前创建代理对象。大部分 Bean 根本不会出现循环依赖白白提前做 AOP 代理浪费 CPU。Spring 设计者选择不提前生成代理把代理逻辑封装进 ObjectFactory延迟触发于是引入三级缓存。模拟流程只有一级 二级缓存、无 AOP可以正常解决循环依赖 A↔B1.new A实例化放入二级缓存2.A 填充属性发现需要 B → new B放入二级缓存3.B 填充属性发现需要 A从二级缓存拿到原始 A 对象4.B 填充属性、初始化完成移入一级缓存5.A 拿到 B填充属性初始化完成移入一级缓存整个流程正常跑完循环依赖解决。注意: 一旦有 AOP 代理两级缓存方案就有问题要么提前全部代理损耗性能要么拿不到代理对象。高频面试问答Q三级缓存是用来解决循环依赖的吗A不完全准确。真正用来 “拿到半成品对象打破闭环” 的是一、二级缓存三级缓存的核心目的是延迟 AOP 代理对象的创建兼顾性能和循环依赖场景。Q那为什么网上都说三级缓存解决循环依赖A因为 Spring 容器要同时兼容两种情况普通 Bean AOP 代理 Bean。在 Spring 完整源码实现中为了兼容 AOP最终使用了三级缓存这套整体方案。所以整体上说 Spring 靠三级缓存机制解决循环依赖但不能理解为 “没有三级缓存就一定不能解决循环依赖”。Q能不能自己写代码实现循环依赖只使用两级缓存A可以前提是你的业务不做 AOP 动态代理。一旦引入代理逻辑两级缓存就需要提前创建代理带来性能损耗。循环依赖方案对比方案对比两级缓存 vs 三级缓存对比维度两级缓存方案一级 二级无 AOPSpring 原生三级缓存方案缓存结构singletonObjects完整 Bean earlySingletonObjects早期对象singletonObjectsearlySingletonObjectssingletonFactories工厂回调三级缓存作用不存在存放ObjectFactorylambda 工厂延迟生成 AOP 代理对象能否解决普通 Bean 循环依赖✅ 可以✅ 可以能否解决带 AOP 代理的循环依赖❌ 不行要么拿不到代理要么全部提前代理✅ 可以工厂回调提前生成代理AOP 代理创建时机实例化后立即创建所有 Bean 都提前代理发生循环依赖被引用时才执行工厂创建代理无循环依赖则正常初始化后代理性能开销高所有单例 Bean 实例化后都提前做代理低只有真正发生循环依赖的 Bean 才提前代理代理对象是否唯一可能重复生成多份代理无工厂缓存二级缓存缓存工厂执行结果保证单例适用场景极简容器、无 AOP、无切面生产级完整 Spring 容器兼容 AOP / 事务 / 切面构造器注入能否解决❌ 不能❌ 不能与缓存级别无关prototype 作用域能否解决❌ 不能❌ 不能不进入缓存三级缓存各层职责速查表缓存名称类型存什么什么时候放入什么时候移除核心作用singletonObjects一级MapString, Object完整就绪的单例 BeanBean 创建全部完成后容器销毁时存放最终成品获取 Bean 优先查这里earlySingletonObjects二级MapString, Object实例化完成、未填充属性的早期对象原始 / 代理三级工厂执行后Bean 放入一级缓存时缓存工厂执行结果避免重复代理singletonFactories三级MapString, ObjectFactory?ObjectFactory 工厂回调Bean 实例化完成后工厂执行后 / Bean 放入一级缓存时延迟生成 AOP 代理只有被引用才执行
延伸阅读

更多相关文章

2026/9/29 14:35:05

PHP文件包含机制解析与安全实践指南

1. PHP外部文件包含机制的本质与历史演进 PHP的外部文件包含机制(File Inclusion)是这门语言最基础也最危险的功能之一。include和require语句自PHP 3时代就已存在,其设计初衷是为了解决代码复用问题。在早期PHP项目中,开发者通过…

2026/9/26 19:44:43

LinkSwift:终极网盘直链下载助手完整技术解析

LinkSwift:终极网盘直链下载助手完整技术解析 【免费下载链接】Online-disk-direct-link-download-assistant 一个基于 JavaScript 的网盘文件下载地址获取工具。基于【网盘直链下载助手】修改 ,支持 百度网盘 / 阿里云盘 / 中国移动云盘 / 天翼云盘 / 迅…

2026/9/29 14:34:58

Codex、Claude Code、OpenCode 统一接入火山方舟配置指南

这一两周,我身边至少有三拨人在折腾同一件事:把 Codex、Claude Code 和 OpenCode 这三款终端里的 AI 编程工具,全部切到火山方舟的模型 API 上。折腾完之后大家发现,其实思路是通的,真正卡人的是几个细节——配置文件长…

2026/9/29 14:34:58

Claude Code与Codex双AI协作工作流:提交前验证清单实践

最近我的开发环境里同时挂了两个AI编程工具:Claude Code 和 Codex。不少朋友问我,这东西装两个是不是浪费,到底哪个好用。这问题我一开始也答不上来,直到某天让 Codex 改完一个函数,它给出了“任务完成”的提示&#x…

2026/9/29 14:34:58

基于Dify搭建“hindsight”复盘助手:从工作流到知识库的完整实践

1. 需求与场景拆解:为什么是“hindsight”先说结论:“hindsight”这个词,直译是“后见之明”,但放在今天的AI应用语境里,它代表的是一类特别有实用价值的产品——“回溯复盘助手”。不管你是个人开发者、产品经理、内容…

2026/9/29 14:34:58

Transformer如何重塑超分辨率重建:从SwinIR到HGFormer

1. 为什么超分辨率重建突然“盯上”了Transformer?过去五年里,我经手过三十多个图像增强类项目,从老式监控视频修复、医学影像放大到卫星图细节还原,几乎全靠CNN打天下。直到2022年中旬,一个客户拿着一张3232的热成像图…

2026/9/29 14:29:58

DeepSeek 财务系统智能化方案:从本地部署到报销自动化

简介:一套DeepSeek与AI大模型驱动的财务管理智能化建设方案PPTX课件,面向企业财务管理者、数字化转型规划人员及财务信息化从业者,聚焦自动化票据处理、智能预算、现金流风控、数据决策支持、税务合规审计等核心模块。资源为1个pptx演示文稿&…

2026/9/29 11:07:23

东莞市品牌网站建设报价常见报错与解决

东莞品牌网站建设报价单背后:一份保姆级建站教程避坑实录 网站做好了没人访问,这大概是很多老板最头疼的事。花了大几万做的品牌站,上线后流量惨淡,比路边摊还冷清。别急着骂外包公司,很多“东莞品牌网站建设报价”里藏着不少猫腻,比如用模板站冒充定制…

2026/9/28 6:05:15

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解 【免费下载链接】spirula-studio Cross-vendor 3D Gaussian Splatting trainer - video to splat to mesh, Vulkan or CUDA. 项目地址: https://gitcode.com/GitHub_Trending/sp/spirula-studio Sp…

2026/9/29 7:00:49

SEO怎么推广速查手册新手避坑实战指南

SEO怎么推广速查手册新手避坑实战指南 模板网站太丑不够用?别急着加滤镜,那是治标不治本。很多老板盯着后台流量掉得眼红,却还在纠结首页Banner的圆角是不是3像素。这就像穿着西装去挖土,姿势不对,努力白费。我整理这份 速查手册…

2026/9/29 0:04:04

AI Evals实战指南:从零搭建LLM应用评估体系与CI/CD集成

1. 为什么AI Evals值得你花时间搞明白做LLM应用的人,迟早会撞上同一堵墙:模型输出飘忽不定,今天答得好好的,明天换个问法就胡说八道。你改了一版提示词,感觉好像好了点,但到底好了多少?说不清。…

2026/9/29 0:04:04

Java采购管理系统实战:从数据库设计到事务一致性

简介:这是一套面向Java Web初学者与课程设计者的采购管理系统完整源码,采用JSP技术搭建,配合MySQL数据库,用于解决企业采购信息的管理问题,适合作为毕业设计、课程大作业或进销存类项目的参考模板。系统实现了用户登录…

2026/9/29 3:53:39

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/29 9:46:12

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/29 6:36:14

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑