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

Spring 源码分析(五)——Spring三级缓存的作用分别是什么?

Spring 的三级缓存是经典面试题,也会看到一些文章讲三级缓存与循环依赖之的关系。那么,三级缓存分别存储的什么呢?他们的作用又分别是什么?

一、一、二级缓存

一级缓存是一个名为 singletonObjectsConcurrentHashMap,用于存储已经创建完成的 Bean。其作用也是最明显的,获取 Bean 时最优先从一级缓存中获取。

二级缓存是一个名为 earlySingletonObjectsConcurrentHashMap,用于存储还未创建完成的 Bean。在循环依赖时,可以从中预先获取未创建完成的 Bean,从而解决循环依赖问题。

二、三级缓存

三级缓存是一个名为 singletonFactoriesConcurrentHashMap,用于存储创建指定 Bean 的工厂。其作用是为了在 AOP 代理时解决循环依赖。

更具体可以说,是为了在 AOP 代理时,既解决循环依赖,又最大满足 Spring 制定的 Bean 生命周期流程。

是的,三级缓存一定程度上说,他提前了 Bean 生命周期的流程。

熟悉 Bean 的生命周期可知,Bean 初始化是经过了实例化(createBeanInstance)、属性注入(populateBean)、后置处理器与生命周期方法(initializeBean)三个步骤处理,最终才得到一个创建完成的 Bean

在循环依赖时,在属性注入(populateBean)步骤就要解决依赖问题,而 AOP 代理是在后置处理器与生命周期方法(initializeBean)步骤中通过后置处理器实现的。如果严格按照 Bean 的这个生命周期执行,依赖注入是无法注入代理之后的对象的。

假设,A 和 B 互相依赖,A 经过 AOP 代理,最终的 BeanAa ,那么严格按照 Bean 的生命周期最终属性注入的结果如下:

getBean——取得 A Bean,在 doCreateBean 方法中开始创建 Bean 操作。

createBeanInstance——实例化 A Bean。

populateBean——为 A Bean 设置参数,并调用 getBean 方法创建 B Bean。

== createBeanInstance——实例化 B Bean。

== populateBean——为 B Bean 设置参数,并调用 getBean 方法获得未构造完全的 A Bean。

initializeBean——为A Bean 执行后置处理器和生命周期方法,并完成 AOP 代理,获得最终的 Aa Bean。

从上面步骤可以发现,B 中注入的是代理之前的 A 对象,而实际上需要被注入的是代理之后的 Aa 对象。

要解决上述 AOP 代理的问题其实也很简单,只要在 createBeanInstance 步骤中完成 AOP 代理其实一切问题就迎刃而解。但是这并不符合 Bean 的生命周期设定,而且循环依赖的场景很少见,在实例化时也无法判断这个 Bean 是否被循环依赖了,直接将 AOP 代理全部提前执行不合适。

最终,Spring 引入了三级缓存,在实例化对象之后,进行属性注入之前,将实现 AOP 代理的步骤封装为 Bean 工厂放进三级缓存。如果这个对象被循环依赖了,则使用工厂提前进行 AOP 代理,如果没有被循环依赖,则这个工厂就不会被使用。

boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&isSingletonCurrentlyInCreation(beanName));
// 是否需要提前暴露
if (earlySingletonExposure) {if (logger.isTraceEnabled()) {logger.trace("Eagerly caching bean '" + beanName +"' to allow for resolving potential circular references");}// 创建bean工厂注入到三级缓存addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
// Initialize the bean instance.
Object exposedObject = bean;
try {populateBean(beanName, mbd, instanceWrapper);exposedObject = initializeBean(beanName, exposedObject, mbd);
}

阅读 getEarlyBeanReference 方法逻辑,可以看到其中遍历找到了实现了SmartInstantiationAwareBeanPostProcessor 接口的后置处理器,并执行了其中的 getEarlyBeanReference 方法,提前完成了代理操作,获取到了代理后的最终得对象。

protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {Object exposedObject = bean;if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {for (BeanPostProcessor bp : getBeanPostProcessors()) {if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);}}}return exposedObject;
}

在获取对象时,getEarlyBeanReference 便按三级缓存的顺序执行操作,如果在三级缓存中获取到 Bean 工厂,则通过工厂获取对象。

protected Object getSingleton(String beanName, boolean allowEarlyReference) {// Quick check for existing instance without full singleton lockObject singletonObject = this.singletonObjects.get(beanName);if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {singletonObject = this.earlySingletonObjects.get(beanName);if (singletonObject == null && allowEarlyReference) {synchronized (this.singletonObjects) {// Consistent creation of early reference within full singleton locksingletonObject = this.singletonObjects.get(beanName);if (singletonObject == null) {singletonObject = this.earlySingletonObjects.get(beanName);if (singletonObject == null) {ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);if (singletonFactory != null) {singletonObject = singletonFactory.getObject();this.earlySingletonObjects.put(beanName, singletonObject);this.singletonFactories.remove(beanName);}}}}}}return singletonObject;
}

相关文章:

Spring 源码分析(五)——Spring三级缓存的作用分别是什么?

Spring 的三级缓存是经典面试题&#xff0c;也会看到一些文章讲三级缓存与循环依赖之的关系。那么&#xff0c;三级缓存分别存储的什么呢&#xff1f;他们的作用又分别是什么&#xff1f; 一、一、二级缓存 一级缓存是一个名为 singletonObjects 的 ConcurrentHashMap&#x…...

Django基于类视图实现增删改查

第一步&#xff1a;导入View from django.views import View 第二步&#xff1a;新建这个基类 class CLS_executer(View):db DB_executerdef get(self, request):executer_list list(self.db.objects.all().values())return HttpResponse(json.dumps(executer_list), conte…...

matplotlib绘图实现中文宋体的两种方法(亲测)

方法一&#xff1a;这种方法我没有测试。 第一步 找宋体字体 &#xff08;win11系统&#xff09; 2.matplotlib字体目录&#xff0c;如果不知道的话&#xff0c;可以通过以下代码查询&#xff1a; matplotlib.matplotlib_fname() 如果你是Anaconda3 安装的matplotlib&#x…...

非常有用的JavaScript高阶面试技巧!

&#x1f340;一、闭包 闭包是指函数中定义的函数&#xff0c;它可以访问外部函数的变量。闭包可以用来创建私有变量和方法&#xff0c;从而保护代码不受外界干扰。 // 例1 function outerFunction() {const privateVariable "私有变量";function innerFunction()…...

windows 安装Linux子系统 Ubuntu 并配置python3

环境说明&#xff1a; Windows 11 Ubuntu 20.04.6 安装步骤以及问题&#xff1a; 1、开启Windows Subsystem for Linux dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart 2、开启虚拟机特性 dism.exe /online /enabl…...

pytorch的pixel_shuffle转tflite文件

torch.pixel_shuffle()是pytorch里面上采样比较常用的方法&#xff0c;但是和tensoflow的depth_to_space不是完全一样的&#xff0c;虽然看起来功能很像&#xff0c;但是细微是有差异的 def tf_pixelshuffle(input, upscale_factor):temp []depth upscale_factor *upscale_f…...

sentinel-dashboard-1.8.0.jar开机自启动脚本

启动阿里巴巴的流控组件控制面板需要运行一个jar包&#xff0c;通常需要运行如下命令&#xff1a; java -server -Xms4G -Xmx4G -Dserver.port8080 -Dcsp.sentinel.dashboard.server127.0.0.1:8080 -Dproject.namesentinel-dashboard -jar sentinel-dashboard-1.8.0.jar &…...

c++堆排序-建堆-插入-删除-排序

本文以大根堆为例&#xff0c;用数组实现&#xff0c;它的nums[0]是数组最大值。 时间复杂度分析&#xff1a; 建堆o(n) 插入删除o(logn) 堆排序O(nlogn) 首先上代码 #include<bits/stdc.h>using namespace std; void down(vector<int>&nums, int idx, i…...

使用代理后pip install 出现ssl错误

window直接设置代理 httphttp://127.0.0.1:7890;httpshttp://127.0.0.1...

护眼灯什么价位的好?最具性价比的护眼台灯推荐

到了晚上光线比较弱&#xff0c;这时候就需要开灯&#xff0c;要是孩子需要近距离看字学习等等&#xff0c;给孩子选择的灯具要特别的重视。护眼灯就是目前颇受学生家长青睐的灯具之一&#xff0c;越来越多的人会购买一个护眼灯给自己的孩子让孩子能够在灯光下学习的时候&#…...

vue event bus 事件总线

vue event bus 事件总线 创建 工程&#xff1a; H:\java_work\java_springboot\vue_study ctrl按住不放 右键 悬着 powershell H:\java_work\java_springboot\js_study\Vue2_3入门到实战-配套资料\01-随堂代码素材\day04\准备代码\08-事件总线-扩展 vue --version vue crea…...

深信服云桌面用户忘记密码后的处理

深信服云桌面用户忘记了密码&#xff0c;分两种情况&#xff0c;一个是忘记了登录深信服云桌面的密码&#xff0c;另外一个是忘记了进入操作系统的密码。 一、忘记了登录深信服云桌面的密码 登录虚拟桌面接入管理系统界面&#xff0c;在用户管理中选择用户后&#xff0c;点击后…...

Cocos Creator3.8 实战问题(一)cocos creator prefab 无法显示内容

问题描述&#xff1a; cocos creator prefab 无法显示内容&#xff0c; 或者只显示一部分内容。 creator编辑器中能看见&#xff1a; 预览时&#xff0c;看不见内容&#xff1a; **问题原因&#xff1a;** prefab node 所在的layer&#xff0c;默认是default。 解决方法&…...

朴素贝叶斯深度解码:从原理到深度学习应用

目录 一、简介贝叶斯定理的历史和重要性定义例子 朴素贝叶斯分类器的应用场景定义例子常见应用场景 二、贝叶斯定理基础条件概率定义例子 贝叶斯公式定义例子 三、朴素贝叶斯算法原理基本构成定义例子 分类过程定义例子 不同变体定义例子 四、朴素贝叶斯的种类高斯朴素贝叶斯&a…...

RUST 每日一省:闭包

Rust中的闭包是一种可以存入外层函数中变量或作为参数传递给其他函数的匿名函数。你可以在一个地方创建闭包&#xff0c;然后在不同的上下文环境中调用该闭包来完成运算。和一般的函数不同&#xff0c;闭包可以从定义它的作用域中捕获值。 语法 闭包由“||”和“{}”组合而成。…...

Ubuntu下文件的解压缩操作:常用zip和unzip

Ubuntu下文件的解\压缩 压缩一个文件夹为zip包&#xff0c;加参数-r&#xff1a; zip -r MyWeb.zip MyWeb需要排除目录里某个文件夹&#xff1f;例如我要去掉node_modules&#xff0c;以显著减小压缩包体积&#xff0c;此时该怎么做&#xff1f; zip -r MyWeb.zip ./MyWeb…...

Linux学习第22天:Linux中断驱动开发(一): 突如其来

Linux版本号4.1.15 芯片I.MX6ULL 大叔学Linux 品人间百味 思文短情长 中断作为驱动开发中很重要的一个概念&#xff0c;在实际的项目实践中经常用到。本节的主要内容包括中断简介、硬件原理分析、驱动程序开发及运行测试。其中驱动程…...

IDEA 2019 Springboot 3.1.3 运行异常

项目场景&#xff1a; 在IDEA 2019 中集成Springboot 3.1.3 框架&#xff0c;运行异常。 <?xml version"1.0" encoding"UTF-8"?><project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSch…...

【JAVA】飞机大战

代码和图片放在这个地址了&#xff1a; https://gitee.com/r77683962/fighting/tree/master 最新的代码运行&#xff0c;可以有两架飞机&#xff0c;分别通过WASD&#xff08;方向&#xff09;&#xff0c;F&#xff08;发子弹&#xff09;&#xff1b;上下左右&#xff08;控…...

Midjourney 生成油画技巧

基本 prompt oil painting, a cute corgi dog surrounded with colorful flowers技法 Pointillism 点描绘法 笔刷比较细&#xff0c;图像更精细 oil painting, a cute corgi dog surrounded with colorful flowers, pontillismImpasto 厚涂绘法 笔刷比较粗&#xff0c;图像…...

Prompt Tuning、P-Tuning、Prefix Tuning的区别

一、Prompt Tuning、P-Tuning、Prefix Tuning的区别 1. Prompt Tuning(提示调优) 核心思想:固定预训练模型参数,仅学习额外的连续提示向量(通常是嵌入层的一部分)。实现方式:在输入文本前添加可训练的连续向量(软提示),模型只更新这些提示参数。优势:参数量少(仅提…...

深入理解JavaScript设计模式之单例模式

目录 什么是单例模式为什么需要单例模式常见应用场景包括 单例模式实现透明单例模式实现不透明单例模式用代理实现单例模式javaScript中的单例模式使用命名空间使用闭包封装私有变量 惰性单例通用的惰性单例 结语 什么是单例模式 单例模式&#xff08;Singleton Pattern&#…...

Linux云原生安全:零信任架构与机密计算

Linux云原生安全&#xff1a;零信任架构与机密计算 构建坚不可摧的云原生防御体系 引言&#xff1a;云原生安全的范式革命 随着云原生技术的普及&#xff0c;安全边界正在从传统的网络边界向工作负载内部转移。Gartner预测&#xff0c;到2025年&#xff0c;零信任架构将成为超…...

【决胜公务员考试】求职OMG——见面课测验1

2025最新版&#xff01;&#xff01;&#xff01;6.8截至答题&#xff0c;大家注意呀&#xff01; 博主码字不易点个关注吧,祝期末顺利~~ 1.单选题(2分) 下列说法错误的是:&#xff08; B &#xff09; A.选调生属于公务员系统 B.公务员属于事业编 C.选调生有基层锻炼的要求 D…...

均衡后的SNRSINR

本文主要摘自参考文献中的前两篇&#xff0c;相关文献中经常会出现MIMO检测后的SINR不过一直没有找到相关数学推到过程&#xff0c;其中文献[1]中给出了相关原理在此仅做记录。 1. 系统模型 复信道模型 n t n_t nt​ 根发送天线&#xff0c; n r n_r nr​ 根接收天线的 MIMO 系…...

iOS性能调优实战:借助克魔(KeyMob)与常用工具深度洞察App瓶颈

在日常iOS开发过程中&#xff0c;性能问题往往是最令人头疼的一类Bug。尤其是在App上线前的压测阶段或是处理用户反馈的高发期&#xff0c;开发者往往需要面对卡顿、崩溃、能耗异常、日志混乱等一系列问题。这些问题表面上看似偶发&#xff0c;但背后往往隐藏着系统资源调度不当…...

Git 3天2K星标:Datawhale 的 Happy-LLM 项目介绍(附教程)

引言 在人工智能飞速发展的今天&#xff0c;大语言模型&#xff08;Large Language Models, LLMs&#xff09;已成为技术领域的焦点。从智能写作到代码生成&#xff0c;LLM 的应用场景不断扩展&#xff0c;深刻改变了我们的工作和生活方式。然而&#xff0c;理解这些模型的内部…...

面试高频问题

文章目录 &#x1f680; 消息队列核心技术揭秘&#xff1a;从入门到秒杀面试官1️⃣ Kafka为何能"吞云吐雾"&#xff1f;性能背后的秘密1.1 顺序写入与零拷贝&#xff1a;性能的双引擎1.2 分区并行&#xff1a;数据的"八车道高速公路"1.3 页缓存与批量处理…...

如何做好一份技术文档?从规划到实践的完整指南

如何做好一份技术文档&#xff1f;从规划到实践的完整指南 &#x1f31f; 嗨&#xff0c;我是IRpickstars&#xff01; &#x1f30c; 总有一行代码&#xff0c;能点亮万千星辰。 &#x1f50d; 在技术的宇宙中&#xff0c;我愿做永不停歇的探索者。 ✨ 用代码丈量世界&…...

Yii2项目自动向GitLab上报Bug

Yii2 项目自动上报Bug 原理 yii2在程序报错时, 会执行指定action, 通过重写ErrorAction, 实现Bug自动提交至GitLab的issue 步骤 配置SiteController中的actions方法 public function actions(){return [error > [class > app\helpers\web\ErrorAction,],];}重写Error…...