深入理解Spring的三级缓存机制
个人名片
🎓作者简介:java领域优质创作者
🌐个人主页:码农阿豪
📞工作室:新空间代码工作室(提供各种软件服务)
💌个人邮箱:[2435024119@qq.com]
📱个人微信:15279484656
🌐个人导航网站:www.forff.top
💡座右铭:总有人要赢。为什么不能是我呢?
- 专栏导航:
码农阿豪系列专栏导航
面试专栏:收集了java相关高频面试题,面试实战总结🍻🎉🖥️
Spring5系列专栏:整理了Spring5重要知识点与实战演练,有案例可直接使用🚀🔧💻
Redis专栏:Redis从零到一学习分享,经验总结,案例实战💐📝💡
全栈系列专栏:海纳百川有容乃大,可能你想要的东西里面都有🤸🌱🚀
目录
- 深入理解Spring的三级缓存机制
- 什么是三级缓存
- 为什么需要三级缓存
- 示例说明
- 三级缓存的实现原理
- 初始化流程
- 关键代码解析
- 三级缓存的工作流程
- 流程图
- 实际应用
- 源码解析
- `DefaultSingletonBeanRegistry` 类
- `getSingleton` 方法
- 总结
深入理解Spring的三级缓存机制
在Spring的源码中,有一个非常重要但常被忽视的设计——三级缓存。三级缓存是Spring解决循环依赖问题的关键机制之一,它在Spring容器初始化Bean时起到了至关重要的作用。本文将详细介绍Spring的三级缓存机制,探讨其实现原理和应用场景,并结合源码解析进一步理解其工作流程。
什么是三级缓存
在Spring的Bean生命周期中,三级缓存主要用于解决循环依赖问题。三级缓存分为以下三个部分:
- 一级缓存(singletonObjects):用于存储完全初始化好的单例Bean。
- 二级缓存(earlySingletonObjects):用于存储提前暴露的单例Bean,主要是为了应对循环依赖问题。
- 三级缓存(singletonFactories):用于存储ObjectFactory,用于在需要时创建Bean。
这三个缓存共同协作,保证了Spring能够处理复杂的Bean依赖关系。
为什么需要三级缓存
在Spring容器启动过程中,如果两个Bean相互依赖,可能会导致循环依赖问题。假设有两个Bean,A和B,A依赖于B,B又依赖于A。如果没有缓存机制,Spring容器在初始化A和B时,会陷入无限循环,最终导致StackOverflowError。三级缓存的引入正是为了解决这一问题,通过提前暴露Bean引用,保证依赖注入的顺利进行。
示例说明
假设有以下两个类:
public class A {private B b;public void setB(B b) {this.b = b;}
}public class B {private A a;public void setA(A a) {this.a = a;}
}
A依赖B,B依赖A,这就是一个典型的循环依赖。在没有缓存机制的情况下,Spring容器会在创建A时发现需要B,但创建B时又需要A,最终陷入死循环。
三级缓存的实现原理
Spring的三级缓存主要在DefaultSingletonBeanRegistry
类中实现,该类包含了以下三个关键属性:
singletonObjects
: 一级缓存,存储完全初始化好的单例Bean。earlySingletonObjects
: 二级缓存,存储提前暴露的单例Bean。singletonFactories
: 三级缓存,存储ObjectFactory,用于在需要时创建Bean。
初始化流程
-
创建Bean实例:
Spring首先会尝试从一级缓存中获取Bean实例,如果没有找到,则会从二级缓存中获取,如果仍未找到,则会从三级缓存中获取。如果三级缓存中也没有,才会创建新的Bean实例。 -
提前暴露Bean引用:
在创建Bean实例的过程中,Spring会将创建中的Bean实例提前暴露到三级缓存中。这一步是通过将Bean包装成一个ObjectFactory来实现的,该ObjectFactory在需要时可以返回Bean实例。 -
处理依赖注入:
在进行依赖注入时,如果依赖的Bean存在循环引用,Spring会从三级缓存中获取提前暴露的Bean实例,以解决循环依赖问题。 -
完成初始化:
在完成依赖注入和其他初始化操作后,Spring会将完全初始化好的Bean实例从三级缓存中移到二级缓存,再从二级缓存移到一级缓存,确保后续可以直接从一级缓存中获取。
关键代码解析
以下是Spring源码中涉及三级缓存的关键代码片段:
protected Object getSingleton(String beanName, boolean allowEarlyReference) {Object singletonObject = this.singletonObjects.get(beanName);if (singletonObject == null && this.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;
}
在getSingleton
方法中,Spring首先从一级缓存中获取Bean实例,如果没有找到,再尝试从二级缓存中获取,最后从三级缓存中获取ObjectFactory来创建Bean实例。
三级缓存的工作流程
三级缓存的工作流程可以分为以下几个步骤:
-
创建Bean实例:
当Spring容器开始创建一个Bean时,会首先尝试从一级缓存中获取,如果没有找到,则会创建新的Bean实例,并将该实例的ObjectFactory放入三级缓存中。 -
提前暴露Bean引用:
在创建Bean实例的过程中,如果发现Bean依赖其他Bean(包括自身依赖),Spring会将创建中的Bean实例提前暴露到三级缓存中,以便其他Bean可以引用。 -
依赖注入:
当Spring发现Bean依赖其他Bean时,会从三级缓存中获取提前暴露的Bean引用,解决循环依赖问题。 -
完成初始化:
在完成依赖注入和其他初始化操作后,Spring会将完全初始化好的Bean实例从三级缓存中移到二级缓存,再移到一级缓存,以确保后续可以直接从一级缓存中获取。
流程图
以下是三级缓存工作流程的简要图示:
+-------------------------+
| |
| 1. 创建Bean实例 |
| |
+-----------+-------------+|v
+-----------+-------------+
| |
| 2. 提前暴露Bean引用 |
| |
+-----------+-------------+|v
+-----------+-------------+
| |
| 3. 依赖注入处理 |
| |
+-----------+-------------+|v
+-----------+-------------+
| |
| 4. 完成初始化 |
| |
+-------------------------+
实际应用
在实际应用中,三级缓存主要用于解决循环依赖问题。例如,在一个复杂的业务场景中,如果多个服务相互依赖,三级缓存可以保证Spring容器能够顺利初始化所有服务,避免循环依赖导致的错误。
源码解析
我们以Spring的源码为例,进一步解析三级缓存的实现。
DefaultSingletonBeanRegistry
类
DefaultSingletonBeanRegistry
是三级缓存机制的核心类。以下是该类的部分源码:
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);// other methods...
}
在DefaultSingletonBeanRegistry
类中,singletonObjects
是一级缓存,earlySingletonObjects
是二级缓存,singletonFactories
是三级缓存。
getSingleton
方法
getSingleton
方法是获取单例Bean实例的核心方法,以下是该方法的简化版源码:
protected Object getSingleton(String beanName, boolean allowEarlyReference) {Object singletonObject = this.singletonObjects.get(beanName);if (singletonObject == null && this.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;
}
在getSingleton
方法中,Spring首先尝试从一级缓存(singletonObjects
)中获取Bean实例。如果没有找到,并且当前Bean正在创建中(循环依赖的情况),则尝试从二级缓存(earlySingletonObjects
)中获取。如果二级缓存中也没有找到,并且允许提前引用(allowEarlyReference
为true
),则从三级缓存(singletonFactories
)中获取ObjectFactory并创建Bean实例。
总结
通过本文的详细解析,我们了解了Spring的三级缓存机制及其在解决循环依赖问题中的重要作用。三级缓存包括一级缓存、二级缓存和三级缓存,它们分别用于存储完全初始化好的单例Bean、提前暴露的单例Bean以及ObjectFactory。三级缓存共同协作,保证了Spring容器能够处理复杂的依赖关系,顺利初始化所有Bean。
三级缓存的引入有效解决了循环依赖问题,使得Spring容器在处理复杂的依赖关系时更加灵活和高效。在实际应用中,理解并掌握三级缓存机制,可以帮助我们更好地解决Bean的依赖注入问题,提升Spring应用的稳定性和可维护性。希望本文能够帮助读者深入理解Spring的三级缓存机制,并在实际开发中应用这一强大的工具。
相关文章:

深入理解Spring的三级缓存机制
个人名片 🎓作者简介:java领域优质创作者 🌐个人主页:码农阿豪 📞工作室:新空间代码工作室(提供各种软件服务) 💌个人邮箱:[2435024119qq.com] 📱…...

LSTM长短时记忆网络【数学+图解】
文章目录 1、简介2、门控机制3、LSTM3.1、概念3.2、公式⭐3.3、特点 4、图解LSTM⭐4.1、RNN4.2、时间链条4.3、记忆单元🔺4.4、LSTM 5、LSTM与GRU的对比6、应用7、训练技巧 🍃作者介绍:双非本科大三网络工程专业在读,阿里云专家博…...

Linux-入门-02
上节我们讲了如何安装虚拟机,本节课讲一些linux的常用命令,首先我们需要做一些配置,我们的centos的镜像是最小版安装,里面什么也没有,所以我们的linux是不能进行联网的,接下来我们就来一步一步联网 1、配置网络 首先我们需要先使用命令查看ip地址,linux中一切皆文件,只能使用命…...

Animate软件基本概念:基本形状、绘制对象及位图
这一篇讲Animate软件中的基本形状、绘制对象及位图三个概念。 FlashASer:AdobeAnimate2021软件零基础入门教程https://zhuanlan.zhihu.com/p/633230084 FlashASer:实用的各种Adobe Animate软件教程https://zhuanlan.zhihu.com/p/675680471 FlashASer&…...

Shell定时上传日志到HDFS
Shell定时上传日志到HDFS 一、任务需求二、实现思路三、具体实现流程3.1 规划文件上传目录3.2 开发 shell 脚本3.3 授予 shell 可执行权限3.4 手动执行查看3.4 定时执行 shell 脚本 一、任务需求 公司在线服务器每天都会产生网站运行日志,为了避免志文件过大&#…...

前端day3-表格
<!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>day3-表格</title> </head> <body&g…...
多进程系列:一个进程对应一个函数
多进程系列:一个进程对应一个函数 这里展示创建两个进程,一个进程用于执行分类模型,另外一个进程用于执行分割模型,以及获取结果的示例。 import multiprocessing import time def classify_data(data):# 这里放置分类任务的代…...
数据清洗与预处理:确保数据质量的关键步骤
数据清洗与预处理:确保数据质量的关键步骤 引言 在大数据时代,数据已成为企业最宝贵的资产。然而,数据的质量直接影响到分析结果和决策的准确性。数据清洗与预处理是确保数据质量的关键步骤,它们包括识别和处理数据中的错误、缺…...

《PostgreSQL 数据库在国内的发展前景》
从DB-engines这张2024年8月的最新排名图上可以看出,PostgreSQL数据库的发展趋势还是非常好的,在国内,PostgreSQL数据库也展现出令人振奋的发展前景,非常明显的一种表现就是腾讯云、人大金仓、阿里云、华为等众多厂商都有基于Postg…...

LVS部署DR集群
介绍 DR(Direct Routing):直接路由,是LVS默认的模式,应用最广泛. 通过为请求报文重新封装一个MAC首部进行转发,源MAC是DIP所在的接口的MAC,目标MAC是某挑选出的RS的RIP所在接口的MAC地址. 整个…...
《Linux运维总结:etcd 3.5.15集群数据备份与恢复》
总结:整理不易,如果对你有帮助,可否点赞关注一下? 更多详细内容请参考:《Linux运维篇:Linux系统运维指南》 一、备份恢复说明 通常, 只需在其中一个节点上对 Etcd 进行快照,即可完成数据备份。但是, 数据恢复时必须要在每个节点上进行。 注意:即便是高可用 Etcd 集群…...

我在杭州的Day30_进程间通信(IPC)——20240805
一、相关练习 1.使用有名管道实现,一个进程用于给另一个进程发消息,另一个进程收到消息后,展示到终端上,并且将消息保存到文件上一份 1.1> 01homework.c #include <myhead.h>int main(int argc, const char *argv[]) …...

FFmpeg推流
目录 一. 环境准备 二. 安装FFmpeg 三. 给docker主机安装docker服务 四. 使用 FFmpeg 进行推流测试 FFmpeg是一个非常强大的多媒体处理工具,它可以用于视频和音频的录制、转换以及流处理。在流处理方面,FFmpeg可以用来推流,即将本地媒体…...
【Rust光年纪】简化文件操作流程:深度剖析多款文件系统操作库
文件系统操作利器:介绍常用的文件操作库 前言 在现代软件开发中,文件系统操作是一个十分常见的需求。为了更加高效地进行文件系统操作,开发人员经常会使用各种文件系统操作库来简化开发流程、提高代码可维护性。本文将介绍几个常用的文件系…...
FFmpeg实现文件夹多视频合并
使用FFmpeg合并文件夹中的多个视频文件,可以通过多种方式来实现,具体取决于你希望如何合并这些视频文件。下面介绍两种常见的方法: 按顺序拼接多个视频文件: 适用于希望将多个视频文件按顺序合并成一个视频文件的情况。 将多个视…...

[设备] 关于手机设备中几种传感器的研究
一、手机设备中三位坐标系概念 X轴的方向:沿着屏幕水平方向从左到右,如果手机如果不是是正方形的话,较短的边需要水平 放置,较长的边需要垂直放置。Y轴的方向:从屏幕的左下角开始沿着屏幕的的垂直方向指向屏幕的顶端Z轴…...
C#通过Modbus读取温度和湿度
使用 C# 通过 RS-485 接口读取温湿度数据并在电脑上显示,需要使用串口通信。假设你的温湿度传感器使用 Modbus RTU 协议,这里提供一个示例代码,使用 System.IO.Ports 命名空间进行串口通信,并使用 Modbus 协议库 NModbus 进行通信…...

海量数据处理商用短链接生成器平台 - 9
第二十六章 短链服务-冗余双写架构删除和更新消费者开发实战 第1集 冗余双写架构-更新短链消费者开发实战 简介: 短链服务-更新短链-消费者开发实战 具体步骤见代码 第2集 冗余双写架构-更新短链消费者链路测试 简介: 冗余双写架构-更新短链消费者链…...

从困境到突破,EasyMR 集群迁移助力大数据底座信创国产化
在大数据时代,企业对数据的依赖程度越来越高。然而,随着业务的不断发展和技术的快速迭代,大数据平台的集群迁移已成为企业数据中台发展途中无法回避的需求。在大数据平台发展初期,国内数据中台市场主要以国外开源 CDH、商业化 CDP…...
【Mysql】第十二章 视图特性(概念+使用)
文章目录 一、概念二、使用1.创建视图2.修改视图会影响基表3.修改基表会影响视图4.删除视图 一、概念 视图不能添加索引,也不能有关联的触发器或者默认值。由于视图和基表用的本质是同一份数据,因此对视图的修改会影响到基表,对基表的修改也…...
OpenLayers 可视化之热力图
注:当前使用的是 ol 5.3.0 版本,天地图使用的key请到天地图官网申请,并替换为自己的key 热力图(Heatmap)又叫热点图,是一种通过特殊高亮显示事物密度分布、变化趋势的数据可视化技术。采用颜色的深浅来显示…...

label-studio的使用教程(导入本地路径)
文章目录 1. 准备环境2. 脚本启动2.1 Windows2.2 Linux 3. 安装label-studio机器学习后端3.1 pip安装(推荐)3.2 GitHub仓库安装 4. 后端配置4.1 yolo环境4.2 引入后端模型4.3 修改脚本4.4 启动后端 5. 标注工程5.1 创建工程5.2 配置图片路径5.3 配置工程类型标签5.4 配置模型5.…...

MongoDB学习和应用(高效的非关系型数据库)
一丶 MongoDB简介 对于社交类软件的功能,我们需要对它的功能特点进行分析: 数据量会随着用户数增大而增大读多写少价值较低非好友看不到其动态信息地理位置的查询… 针对以上特点进行分析各大存储工具: mysql:关系型数据库&am…...

ETLCloud可能遇到的问题有哪些?常见坑位解析
数据集成平台ETLCloud,主要用于支持数据的抽取(Extract)、转换(Transform)和加载(Load)过程。提供了一个简洁直观的界面,以便用户可以在不同的数据源之间轻松地进行数据迁移和转换。…...

从零实现STL哈希容器:unordered_map/unordered_set封装详解
本篇文章是对C学习的STL哈希容器自主实现部分的学习分享 希望也能为你带来些帮助~ 那咱们废话不多说,直接开始吧! 一、源码结构分析 1. SGISTL30实现剖析 // hash_set核心结构 template <class Value, class HashFcn, ...> class hash_set {ty…...

涂鸦T5AI手搓语音、emoji、otto机器人从入门到实战
“🤖手搓TuyaAI语音指令 😍秒变表情包大师,让萌系Otto机器人🔥玩出智能新花样!开整!” 🤖 Otto机器人 → 直接点明主体 手搓TuyaAI语音 → 强调 自主编程/自定义 语音控制(TuyaAI…...
爬虫基础学习day2
# 爬虫设计领域 工商:企查查、天眼查短视频:抖音、快手、西瓜 ---> 飞瓜电商:京东、淘宝、聚美优品、亚马逊 ---> 分析店铺经营决策标题、排名航空:抓取所有航空公司价格 ---> 去哪儿自媒体:采集自媒体数据进…...

ArcGIS Pro制作水平横向图例+多级标注
今天介绍下载ArcGIS Pro中如何设置水平横向图例。 之前我们介绍了ArcGIS的横向图例制作:ArcGIS横向、多列图例、顺序重排、符号居中、批量更改图例符号等等(ArcGIS出图图例8大技巧),那这次我们看看ArcGIS Pro如何更加快捷的操作。…...

使用LangGraph和LangSmith构建多智能体人工智能系统
现在,通过组合几个较小的子智能体来创建一个强大的人工智能智能体正成为一种趋势。但这也带来了一些挑战,比如减少幻觉、管理对话流程、在测试期间留意智能体的工作方式、允许人工介入以及评估其性能。你需要进行大量的反复试验。 在这篇博客〔原作者&a…...
C++课设:简易日历程序(支持传统节假日 + 二十四节气 + 个人纪念日管理)
名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 专栏介绍:《编程项目实战》 目录 一、为什么要开发一个日历程序?1. 深入理解时间算法2. 练习面向对象设计3. 学习数据结构应用二、核心算法深度解析…...