news 2026/5/11 6:38:44

spring为什么使用三级缓存而不是两级?

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
spring为什么使用三级缓存而不是两级?

Spring 使用三级缓存(而不是两级)来解决循环依赖,主要目的是兼容 AOP(动态代理)场景,同时保持 Bean 创建过程的语义一致性扩展性

如果只用两级缓存,在大多数普通属性注入的循环依赖场景下确实可以工作,但一旦引入 AOP(@Transactional、@Async、@Cacheable 等),就会出现非常严重的问题提前暴露的对象和最终容器里的对象不是同一个(类型/引用不一致),导致运行时类型转换异常、代理失效、事务不生效等。

三级缓存各自职责(DefaultSingletonBeanRegistry)

缓存层级Map 类型存放的内容什么时候放入什么时候取出并移除核心目的
一级缓存 singletonObjects普通 bean(成品)完全初始化好的 bean(可能已代理)初始化完成、放入成品后几乎所有 getBean 最终都走这里成品缓存,正常使用的地方
二级缓存 earlySingletonObjects早期普通对象(半成品)已实例化但还没完成属性填充和初始化的原始对象从三级缓存拿到并暴露早期引用后被其他 bean 依赖时取出,移到一级防止重复创建同一个早期对象
三级缓存 singletonFactoriesObjectFactory(lambda 工厂)生成早期引用的工厂(通常是 getEarlyBeanReference 的 lambda)put 时创建 bean 实例后立即放入第一次被循环依赖时调用 getObject() → 生成早期对象(或代理对象)→ 放入二级延迟决定是否要 AOP 代理的关键点

为什么二级缓存不够?(最核心原因)

假设我们只有一级 + 二级(成品 + 早期普通对象):

A → B → A (A 被 AOP 代理)
  1. 创建 A → 实例化(new A) → 放入二级缓存(早期 A,原始对象)
  2. A 需要注入 B → 创建 B
  3. B 需要注入 A → 从二级缓存拿到原始 A(还没代理)
  4. B 完成 → 放入一级缓存
  5. A 继续 → 填充属性、初始化、AOP 后生成代理对象 proxyA
  6. proxyA 放入一级缓存

问题来了

  • B 手里拿到的 A 是原始 A
  • 容器最终暴露的是proxyA
  • B 拿到的引用和最终容器里的 bean 不是同一个对象!

这会导致:

  • B 调用 A 的方法时绕过了代理 → 事务/日志/权限等切面全部失效
  • 类型检查失败(有些地方强转成代理接口会报错)
  • equals()/hashCode() 异常行为

三级缓存如何解决这个问题?

关键就在第三级:它放的不是对象本身,而是一个 ObjectFactory(lambda),这个 lambda 会在真正被别人依赖的时候才执行:

// 大致伪代码protectedObjectgetEarlyBeanReference(StringbeanName,RootBeanDefinitionmbd,Objectbean){returngetSingleton(beanName,()->{// 这里才是真正决定要不要代理的地方returnapplyBeanPostProcessorsBeforeInitialization(bean,beanName);// → post-processor 可能返回代理对象});}

流程变成:

  1. 创建 A → 实例化 → 放入三级缓存(一个 lambda:getEarlyBeanReference)
  2. A 需要 B → 创建 B
  3. B 需要 A → 从三级缓存拿到 lambda →执行 lambda→ 得到早期引用(如果是 AOP 场景,这里就会提前生成代理对象
  4. 把这个早期引用(可能是代理)放入二级缓存,同时从三级移除
  5. B 拿到的是已经代理好的 A(proxyA)
  6. A 继续后续流程,最终 proxyA 放入一级缓存

结果:B 拿到的 A 和最终容器里的 A 是同一个对象(都是 proxyA),语义一致。

总结一句话

Spring 用三级缓存而不是两级,核心是为了在提前暴露引用时仍然能正确应用 AOP 动态代理,保证“别人提前拿到的引用”和“最终容器里的 bean”是同一个对象。

如果你的项目完全关闭 AOP(never proxy),理论上两级缓存就够了(很多手写 IoC 框架就是这么干的)。但 Spring 要做成通用框架,必须兼容 AOP,所以必须用三级。

你项目里遇到过因为循环依赖 + AOP 导致的诡异 bug 吗?或者你更倾向于“能不循环就不循环”的设计哲学?

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/3 1:23:07

HBuilderX安装后CSS预处理器配置操作指南

以下是对您提供的博文内容进行 深度润色与结构化重构后的技术文章 。整体风格更贴近一位资深前端工程师在技术社区中自然、专业、有温度的分享,去除了模板化表达和AI痕迹,强化了逻辑递进、实战细节与教学引导性,同时严格遵循您提出的全部优…

作者头像 李华
网站建设 2026/5/3 2:38:04

GPU内存检测工具:全面排查显存稳定性问题的专业指南

GPU内存检测工具:全面排查显存稳定性问题的专业指南 【免费下载链接】memtest_vulkan Vulkan compute tool for testing video memory stability 项目地址: https://gitcode.com/gh_mirrors/me/memtest_vulkan 您是否遇到过视频渲染时画面突然出现彩色噪点&a…

作者头像 李华
网站建设 2026/5/3 13:28:08

Arduino CLI精通指南:命令行开发实战进阶

Arduino CLI精通指南:命令行开发实战进阶 【免费下载链接】arduino-cli Arduino command line tool 项目地址: https://gitcode.com/gh_mirrors/ar/arduino-cli 一、核心功能概览:重新定义Arduino开发流程 Arduino CLI作为官方命令行工具&#x…

作者头像 李华
网站建设 2026/5/3 13:27:42

解密高效翻译:Crow Translate如何引发效率革命

解密高效翻译:Crow Translate如何引发效率革命 【免费下载链接】crow-translate Crow Translate - 一个用C/Qt编写的简单轻量级翻译器,支持使用Google、Yandex、Bing等API进行文本翻译和朗读。 项目地址: https://gitcode.com/gh_mirrors/cr/crow-tran…

作者头像 李华
网站建设 2026/5/11 2:15:34

深度测评9个AI论文网站,专科生轻松搞定毕业论文!

深度测评9个AI论文网站,专科生轻松搞定毕业论文! AI 工具如何助力专科生轻松应对毕业论文 在当前的学术环境中,AI 工具已经成为许多学生解决论文写作难题的重要助手。尤其是对于专科生而言,面对繁重的学业压力和对论文格式、内容…

作者头像 李华