news 2026/9/25 12:01:13

一文讲清楚Spring中的三级缓存(附完整流程演示)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
一文讲清楚Spring中的三级缓存(附完整流程演示)

01-三级缓存的核心目的

Spring 三级缓存主要解决两个核心问题:

  • 单例 Bean 的循环依赖问题:Bean A 依赖 Bean B,Bean B 又依赖 Bean A
  • AOP代理与循环依赖的兼容问题:在循环依赖场景下,如何确保注入的是正确的代理对象

02-三级缓存的完整定义

public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry { // 第一级缓存:完全初始化好的单例Bean private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); // 第二级缓存:提前暴露的单例Bean(已实例化但未完成初始化) private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16); // 第三级缓存:单例工厂(用于创建Bean的早期引用,可能包含代理对象) private final Map<String, ObjectFactory<?>> singletonFactories = new ConcurrentHashMap<>(16); // 正在创建中的Bean名称集合 private final Set<String> singletonsCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap<>(16)); // 已注册的单例Bean名称集合 private final Set<String> registeredSingletons = new LinkedHashSet<>(256); }

03-各级缓存详细说明

3.1 一级缓存:singletonObjects

一级缓存存储完全初始化完成的 Bean 实例

特点:

  • Bean 已经完成了所有初始化步骤:
    • 实例化(调用构造方法)
    • 属性填充(@Autowired 注入)
    • 初始化(@PostConstruct、InitializingBean)
    • 如果有 AOP 代理,存储的是代理对象
  • 线程安全,支持并发访问
  • 存储的 Bean 可以直接被应用程序使用

3.2二级缓存:earlySingletonObjects

二级缓存存储提前暴露的 Bean 实例(已实例化但未完成属性注入)

特点:

  • Bean 已经实例化,但尚未完成属性注入
  • 主要是为了解决循环依赖而临时存储
  • 如果 Bean 需要 AOP 代理,这里存储的是代理对象
  • 从三级缓存的 ObjectFactory 创建后移入二级缓存

3.3 三级缓存:singletonFactories

三级缓存存储ObjectFactory 工厂对象,用于创建 Bean 的早期引用

特点:

  • 存储的是ObjectFactory<?>而不是 Bean 实例
  • 调用getObject()方法可以创建 Bean 的早期引用
  • 核心作用:解决 AOP 代理与循环依赖的结合问题
  • 在 Bean 实例化后、属性注入前放入

04-三级缓存的关键方法详解

4.1addSingletonFactory():添加三级缓存

将创建 Bean 的工厂放入三级缓存,为循环依赖提供出口

protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) { synchronized (this.singletonObjects) { if (!this.singletonObjects.containsKey(beanName)) { // 放入三级缓存 this.singletonFactories.put(beanName, singletonFactory); // 从二级缓存移除(保证数据一致性) this.earlySingletonObjects.remove(beanName); this.registeredSingletons.add(beanName); } } }

4.2 getSingleton():获取单例Bean

涉及所有三级缓存:按 1→2→3 顺序查找

三级缓存的查询逻辑:

protected Object getSingleton(String beanName, boolean allowEarlyReference) { // 快速检查一级缓存(完全初始化的Bean) Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { synchronized (this.singletonObjects) { // 检查二级缓存(提前暴露的Bean) singletonObject = this.earlySingletonObjects.get(beanName); if (singletonObject == null && allowEarlyReference) { // 检查三级缓存(Bean工厂) ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); if (singletonFactory != null) { // 调用工厂方法获取Bean的早期引用 singletonObject = singletonFactory.getObject(); // 将获取到的Bean放入二级缓存 this.earlySingletonObjects.put(beanName, singletonObject); // 从三级缓存移除 this.singletonFactories.remove(beanName); } } } } return singletonObject; }

4.3 getEarlyBeanReference():获取早期Bean引用

从三级缓存 获取工厂时调用,生成早期引用(可能是代理对象)

protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) { Object exposedObject = bean; // 如果BeanDefinition不是合成的(非基础设施Bean)且存在InstantiationAwareBeanPostProcessor if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { for (BeanPostProcessor bp : getBeanPostProcessors()) { if (bp instanceof SmartInstantiationAwareBeanPostProcessor) { SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp; // 关键:后置处理器可能返回代理对象 exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName); } } } return exposedObject; }

05-完整流程演示:A ↔ B 循环依赖 + AOP

假设有两个Service,且A需要AOP代理:

@Service @Transactional // A需要事务代理 public class ServiceA { @Autowired private ServiceB serviceB; public void methodA() { // 业务逻辑 } } @Service public class ServiceB { @Autowired private ServiceA serviceA; public void methodB() { // 业务逻辑 } }

详细执行流程:

06-注意事项

6.1 构造器注入无法使用三级缓存

构造器调用时对象还未创建,无法放入三级缓存,因此无法解决循环依赖。

@Component public class ServiceA { private final ServiceB serviceB; @Autowired // 构造器注入 public ServiceA(ServiceB serviceB) { this.serviceB = serviceB; // 构造时就依赖B,但B可能还未创建 // 此时ServiceA还未实例化完成,ServiceA 无法放入三级缓存 } }

6.2 原型Bean的循环依赖

原型Bean不放入三级缓存,每次请求都创建新实例

@Scope("prototype") @Component public class PrototypeA { @Autowired private PrototypeB b; } @Scope("prototype") @Component public class PrototypeB { @Autowired private PrototypeA a; }

Spring会直接抛出异常:BeanCurrentlyInCreationException: Error creating bean with name 'prototypeA': Requested bean is currently in creation

6.3 @Async方法的特殊处理

@Async也是通过AOP实现的,但处理时机不同;
需要在initializeBean阶段创建代理,可能影响循环依赖

@Service public class AsyncService { @Async public void asyncMethod() { // 异步方法 } @Autowired private AnotherService anotherService; }

07-总结

缓存级别存储内容生命周期主要作用
一级缓存完全初始化的BeanBean整个生命周期提供最终可用的Bean
二级缓存早期Bean(已实例化未初始化)创建过程中临时存在解决循环依赖,避免重复创建
三级缓存ObjectFactory工厂对象Bean实例化后到初始化前处理AOP代理,确保返回正确的代理对象

核心思想:通过懒加载的ObjectFactory延迟决策,只有在真正发生循环依赖时才创建代理对象,既解决了循环依赖问题,又保持了Bean生命周期的完整性,同时支持AOP等增强功能。

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

canvas绘制图片

项目中有一个点击图片&#xff0c;在点击区域画圆标注的需求&#xff0c;需要知道点击的位置&#xff0c;并判断位置是否正确&#xff0c;类似“找不同”小游戏。这种就需要用到canvas了基本说明canvas 是 一个 HTML 元素&#xff0c;用于绘制图形和图像&#xff0c;通过 JavaS…

作者头像 李华
网站建设 2026/9/25 8:52:25

常用日志积累

1)正则查询cat protocol-2025-12-12.log | grep -E PRT_PAY_BUY_*|PAY_NOT_CAN_CHARGE|PRT_GIFT_FUN_SYN | grep 104936含义:查询还有购买的&#xff0c;购买不成功的&#xff0c;同步购买次数协议。

作者头像 李华
网站建设 2026/9/24 21:35:02

2025CTF逆向工程“新范式”:当Jadx装上AI大脑,逆向效率提升

随着人工智能技术的快速发展&#xff0c;2025年以来&#xff0c;Model Context Protocol&#xff08;MCP&#xff09;逐渐成为逆向工程领域的新宠。各类主流工具如IDA、Jadx等纷纷推出对应的MCP插件&#xff0c;通过结合大模型能力&#xff0c;大幅提升了代码分析效率。尽管早期…

作者头像 李华
网站建设 2026/9/24 9:34:43

2025年AI学习必备网站与高效上手全攻略:从认知到实战的完整路径

在塔猴、Coursera、edX和B站构成的多元化学习生态中&#xff0c;超过2500万用户正在通过系统化课程掌握AI技能。而Perplexity、Midjourney等前沿AI工具则从应用端简化了入门门槛。如今&#xff0c;AI技术已不再仅仅是一个前沿概念&#xff0c;它正逐渐转变为一项必备的职场技能…

作者头像 李华
网站建设 2026/9/24 9:34:34

oracle迁移到sqlserver的注意点

迁移工具可以考虑结合&#xff1a; 1.kdts在转换sysdate默认值字段的时候会错误&#xff0c;在sqlserver中可以考虑改用getdate()或sysdatetime()&#xff0c;精度不同。分区表迁移到sqlserver自动转成普通表。 2.ssma for oracle&#xff0c;需vpn下载 https://www.microsof…

作者头像 李华
网站建设 2026/9/24 9:34:25

从零到精通大型语言模型(LLM):一篇必藏的技术全景指南

大型语言模型(LLM)代表从指令编程到学习型智能的范式革命&#xff0c;通过海量文本数据训练而非明确规则。从ELIZA到GPT-4&#xff0c;LLM经历了六个数量级的参数增长。其工作流程包括分词、嵌入和Transformer三个步骤&#xff0c;通过自注意力机制理解语言模式。尽管面临幻觉、…

作者头像 李华