当前位置: 首页 > news >正文

一文讲清楚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等增强功能。

http://www.cnnetsun.cn/news/137068.html

相关文章:

  • 24、COM+ 事务管理与补偿资源管理器详解
  • YOLO深度学习模型的训练参数配置与优化
  • 数字孪生可视化模板怎么用?5大行业Demo拆解,帮你快速复用提效
  • 必藏!程序员转型AI大模型:机遇、路径与成功率拆解
  • 《智构空间:AIOS 与全时域 3D 交互范式》第 0 篇:前言 —— 触摸语义的厚度
  • 如何将照片从 Android 传输到 Android
  • 前端Vue使用js-audio-plugin实现录音功能
  • 测试用例之翻页功能详解
  • 音乐平台歌曲盗版维权全攻略:权利卫士录屏取证+可信时间戳认证实操指南
  • 根据您提供的 package.json 片段,涉及的 @vue/cli-plugin-babel 和 @vue/cli-service 版本为 ~4.2.0。以下是针对该版本的详细解决方案,结合相关依
  • electron-egg打包win7
  • 8种网络故障分析及测试命令大全
  • 新人必看盘点知名CTF练习靶场,从零基础入门到精通,收藏这一篇就够了!
  • Pythonselenium自动化测试实战项目
  • 关于Comtos Linux (朱雀)主体源码的选择
  • 超级Mini小车功能说明
  • STC32G12单片机替换成STC32F12单片机,直接替换的结果
  • SIEMENS 6SL3210-1PE33-0CL0 变频器
  • 软件测试常用的7种方法,最后一个是升职加薪关键!(零基础小白转行IT互联网高效进阶)
  • 【RTOS】EasyLog的移植与使用
  • 在数据库里玩“平行宇宙”:MatrixOne Data Branch 让数据也拥有Git 的分支/合并/对比/回滚(含跨集群同步)
  • 基于单片机的全自动洗衣机系统的设计
  • 5.6 模型部署与智能体集成实战
  • 基于单片机的球赛计分牌的设计
  • ArcGIS Pro 从入门到实战基础篇(10):地图菜单
  • Kotaemon与Redis/Memcached集成:构建高速缓存层
  • 【鸿蒙三方库编译】lycium_plusplus(lycium++)高效完成鸿蒙C/C++编译
  • 2025年度GEO服务商权威甄选指南:技术深度与商业价值的双重考量
  • 收藏备用!Java程序员转AI大模型:从技术沉淀到AI爆发的进阶之路
  • Python 爬虫实战:Session 会话维持爬取需登录内容