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

Spring三级缓存通俗易懂讲解

Spring三级缓存通俗易懂讲解

一、用盖房子的比喻理解三级缓存

想象一下我们要盖两栋房子:A房B房,但是有个特殊要求:

  • A房需要B房的钥匙才能装修
  • B房需要A房的钥匙才能装修
  • 这就是循环依赖

Spring的三级缓存就像是三个不同阶段的钥匙保管箱

1. 一级缓存:成品房钥匙箱(singletonObjects)

  • 作用:存放完全装修好的房子的钥匙
  • 特点:房子已经可以入住了,所有设施都齐全
  • 比喻:房产证已经办好,可以随时交房

2. 二级缓存:半成品房钥匙箱(earlySingletonObjects)

  • 作用:存放正在装修的房子的临时钥匙
  • 特点:房子框架已经搭好,但内部装修还没完成
  • 比喻:房子主体结构完成,可以临时参观但不能入住

3. 三级缓存:施工队联系方式箱(singletonFactories)

  • 作用:存放施工队的联系方式,可以随时联系他们继续装修
  • 特点:知道怎么把毛坯房变成精装修房
  • 比喻:建筑公司的项目经理电话

二、具体盖房过程(循环依赖解决)

场景:A房和B房互相需要对方的钥匙

// A房需要B房的钥匙
@Service
public class HouseA {private HouseB houseB;  // 需要B房的钥匙public void setHouseB(HouseB houseB) {this.houseB = houseB;}
}// B房需要A房的钥匙  
@Service
public class HouseB {private HouseA houseA;  // 需要A房的钥匙public void setHouseA(HouseA houseA) {this.houseA = houseA;}
}

盖房步骤详解:

第1步:开始盖A房
  • Spring说:“我要盖A房了!”
  • 先把A房施工队联系方式存到三级缓存(施工队联系方式箱)
  • 现在A房还是个毛坯房,只有框架
// Spring内部操作:
三级缓存.put("houseA", () -> {// 这个lambda就是施工队,知道怎么装修A房return 装修A();
});
第2步:A房装修需要B房钥匙
  • A房施工队说:“我需要B房的钥匙才能继续装修A房”
  • Spring去检查:B房盖好了吗?
第3步:开始盖B房
  • Spring发现B房还没开始盖,于是:“我要盖B房了!”
  • B房施工队联系方式存到三级缓存
  • B房现在也是毛坯房
第4步:B房装修需要A房钥匙
  • B房施工队说:“我需要A房的钥匙才能装修B房”
  • Spring去检查A房状态
第5步:关键操作 - 临时借用A房钥匙
  • Spring发现A房在三级缓存有施工队联系方式
  • 联系A房施工队:“先给我一个临时的A房钥匙”
  • 施工队快速装修出一个临时可用的A房(早期引用)
  • 把这个临时A房钥匙存到二级缓存(半成品房钥匙箱)
// Spring内部操作:
Object earlyA = 三级缓存.get("houseA").getObject();  // 调用施工队
二级缓存.put("houseA", earlyA);  // 临时钥匙存到二级缓存
第6步:B房继续装修
  • Spring把临时A房钥匙交给B房施工队
  • B房施工队说:“好的,我有A房钥匙了,可以继续装修B房了”
  • B房装修完成,钥匙存到一级缓存(成品房钥匙箱)
第7步:A房继续装修
  • Spring说:“B房已经盖好了,这是B房的钥匙”
  • A房施工队拿到B房钥匙,继续完成A房装修
  • A房装修完成,钥匙也存到一级缓存
第8步:清理临时钥匙
  • 从二级缓存和三级缓存中移除A房和B房的临时信息
  • 现在一级缓存中有两把完整的钥匙:A房和B房

三、代码层面的具体流程

三级缓存在Spring中的实际代码

// Spring内部的三个缓存Map(简化版)
public class DefaultSingletonBeanRegistry {// 一级缓存:成品Beanprivate final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);// 二级缓存:早期引用(半成品)private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);// 三级缓存:Bean工厂(施工队)private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
}

具体解决流程代码模拟

// 模拟Spring解决循环依赖的过程
public class SpringCacheDemo {public static void main(String[] args) {// 模拟三级缓存Map<String, Object> 一级缓存 = new HashMap<>();  // 成品房Map<String, Object> 二级缓存 = new HashMap<>();  // 半成品房Map<String, Supplier<Object>> 三级缓存 = new HashMap<>();  // 施工队System.out.println("=== 开始创建A房 ===");// 第1步:A房开始施工,记录施工队三级缓存.put("A房", () -> {System.out.println("  A房施工队:我正在装修A房...");Object a房实例 = new Object();  // 创建A房实例System.out.println("  A房施工队:A房装修完成!");return a房实例;});System.out.println("  A房施工队联系方式已记录到三级缓存");System.out.println("\n=== A房需要B房钥匙 ===");// 第2步:检查B房状态if (!一级缓存.containsKey("B房")) {System.out.println("  B房还没盖,先盖B房");// 第3步:B房开始施工三级缓存.put("B房", () -> {System.out.println("  B房施工队:我正在装修B房...");Object b房实例 = new Object();System.out.println("  B房施工队:B房装修完成!");return b房实例;});System.out.println("  B房施工队联系方式已记录到三级缓存");System.out.println("\n=== B房需要A房钥匙 ===");// 第4步:B房需要A房钥匙,检查A房状态if (三级缓存.containsKey("A房")) {System.out.println("  A房有施工队,先要个临时钥匙");// 第5步:获取A房临时钥匙Supplier<Object> a房施工队 = 三级缓存.get("A房");Object 临时A= a房施工队.get();  // 快速装修临时A房二级缓存.put("A房", 临时A);System.out.println("  A房临时钥匙已存到二级缓存");// 第6步:B房继续施工Supplier<Object> b房施工队 = 三级缓存.get("B房");Object 成品B= b房施工队.get();一级缓存.put("B房", 成品B);System.out.println("  B房成品钥匙已存到一级缓存");// 第7步:A房继续施工(现在有B房钥匙了)a房施工队 = 三级缓存.get("A房");Object 成品A= a房施工队.get();一级缓存.put("A房", 成品A);System.out.println("  A房成品钥匙已存到一级缓存");// 第8步:清理缓存二级缓存.remove("A房");三级缓存.remove("A房");三级缓存.remove("B房");System.out.println("  临时缓存已清理");}}System.out.println("\n=== 最终结果 ===");System.out.println("一级缓存(成品房):" + 一级缓存.keySet());System.out.println("二级缓存(半成品):" + 二级缓存.keySet());System.out.println("三级缓存(施工队):" + 三级缓存.keySet());}
}

四、为什么需要三级缓存?

如果只有两级缓存会怎样?

假设我们只有一级缓存(成品房)二级缓存(施工队)

  1. A房开始施工 → 施工队存到二级缓存
  2. A房需要B房钥匙
  3. B房开始施工 → 施工队存到二级缓存
  4. B房需要A房钥匙
  5. 从二级缓存找到A房施工队
  6. 问题:如果直接调用施工队,A房会被完整创建两次!

三级缓存的好处:

  1. 避免重复创建:通过二级缓存存放早期引用,确保同一个Bean只被创建一次
  2. 支持AOP代理:如果需要AOP代理,三级缓存可以确保代理对象的一致性
  3. 性能优化:减少不必要的对象创建和初始化

五、实际Spring源码中的关键方法

getSingleton() 方法(找钥匙)

// 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;
}

addSingletonFactory() 方法(记录施工队)

// 记录施工队联系方式
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {synchronized (this.singletonObjects) {if (!this.singletonObjects.containsKey(beanName)) {// 存到三级缓存(施工队联系方式箱)this.singletonFactories.put(beanName, singletonFactory);this.earlySingletonObjects.remove(beanName);}}
}

六、总结

记住这个简单的比喻:

  • 一级缓存 = 成品房钥匙箱(可以入住的房子)
  • 二级缓存 = 半成品房钥匙箱(正在装修的房子)
  • 三级缓存 = 施工队联系方式箱(知道怎么装修房子的施工队)

当遇到循环依赖时,Spring通过临时借用半成品钥匙的方式,让两个互相依赖的房子都能顺利盖完!

这样理解是不是就清楚多了?

http://www.dtcms.com/a/581467.html

相关文章:

  • Windows10/Windows11家庭版系统关闭自动更新
  • 基于 Flink CDC 的 MySQL → Kafka Streaming ELT 实战
  • Redis内存回收,缓存问题
  • 一项基于高灵敏度sCMOS相机的光镊成像实验
  • wordpress 调用分类名上海不限关键词优化
  • 运维高级故障排除与恢复-SysRq
  • word插入的图片显示不完全解决方法
  • 北京响应式h5网站开发网站优化排名推广
  • 面向强化学习的状态空间建模:RSSM的介绍和PyTorch实现(3)
  • 网站建设如何上传图片阿里云虚拟主机可以做几个网站吗
  • 文章精读:(CVPR2024)DemosaicFormer:用于HybridEVS相机的粗到细去马赛克网络
  • 红帽Linux-基本管理存储
  • LangGraph基础教程(1)---LangGraph的简介
  • IDEA + Spring Boot 的三种热加载方案
  • 有哪些摄影网站甘肃省建设厅官网
  • presto安装与使用
  • 信息论(三):霍夫曼编码
  • 002-GD32L235KBQ6 Keil工程移植J-Link RTT
  • 手机网站建设 苏州企石镇做网站
  • java的设计模式之桥接模式(Bridge)
  • 【Unity踩坑】Error MSB3774: 找不到 SDK“WindowsMobile, Version=10.0.26100.0
  • 网站二维码可以做长按识别吗深圳专业网站建设制作价格低
  • 图片优化 上传图片压缩 npm包支持vue(react)框架开源插件 支持在线与本地
  • React Native CLI的搭建
  • 世界互联网大会乌镇峰会:共话数字未来新可能
  • TeamCity更新包
  • 第8届 AiDD峰会 深圳 | “AI+领域”线:解锁未来科技新图景
  • 网站搭建服务器需要多少钱网站运营seo招聘
  • 结构自由度
  • Effective Python 第49条:用__init_subclass__记录现有的子类