Spring 源码分析(五)——Spring三级缓存的作用分别是什么?
Spring 的三级缓存是经典面试题,也会看到一些文章讲三级缓存与循环依赖之的关系。那么,三级缓存分别存储的什么呢?他们的作用又分别是什么?
一、一、二级缓存
一级缓存是一个名为 singletonObjects 的 ConcurrentHashMap,用于存储已经创建完成的 Bean。其作用也是最明显的,获取 Bean 时最优先从一级缓存中获取。
二级缓存是一个名为 earlySingletonObjects 的 ConcurrentHashMap,用于存储还未创建完成的 Bean。在循环依赖时,可以从中预先获取未创建完成的 Bean,从而解决循环依赖问题。
二、三级缓存
三级缓存是一个名为 singletonFactories 的 ConcurrentHashMap,用于存储创建指定 Bean 的工厂。其作用是为了在 AOP 代理时解决循环依赖。
更具体可以说,是为了在
AOP代理时,既解决循环依赖,又最大满足Spring制定的Bean生命周期流程。是的,三级缓存一定程度上说,他提前了
Bean生命周期的流程。
熟悉 Bean 的生命周期可知,Bean 初始化是经过了实例化(createBeanInstance)、属性注入(populateBean)、后置处理器与生命周期方法(initializeBean)三个步骤处理,最终才得到一个创建完成的 Bean。
在循环依赖时,在属性注入(populateBean)步骤就要解决依赖问题,而 AOP 代理是在后置处理器与生命周期方法(initializeBean)步骤中通过后置处理器实现的。如果严格按照 Bean 的这个生命周期执行,依赖注入是无法注入代理之后的对象的。
假设,A 和 B 互相依赖,A 经过 AOP 代理,最终的 Bean 为 Aa ,那么严格按照 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 的三级缓存是经典面试题,也会看到一些文章讲三级缓存与循环依赖之的关系。那么,三级缓存分别存储的什么呢?他们的作用又分别是什么? 一、一、二级缓存 一级缓存是一个名为 singletonObjects 的 ConcurrentHashMap&#x…...
Django基于类视图实现增删改查
第一步:导入View from django.views import View 第二步:新建这个基类 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绘图实现中文宋体的两种方法(亲测)
方法一:这种方法我没有测试。 第一步 找宋体字体 (win11系统) 2.matplotlib字体目录,如果不知道的话,可以通过以下代码查询: matplotlib.matplotlib_fname() 如果你是Anaconda3 安装的matplotlib&#x…...
非常有用的JavaScript高阶面试技巧!
🍀一、闭包 闭包是指函数中定义的函数,它可以访问外部函数的变量。闭包可以用来创建私有变量和方法,从而保护代码不受外界干扰。 // 例1 function outerFunction() {const privateVariable "私有变量";function innerFunction()…...
windows 安装Linux子系统 Ubuntu 并配置python3
环境说明: Windows 11 Ubuntu 20.04.6 安装步骤以及问题: 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里面上采样比较常用的方法,但是和tensoflow的depth_to_space不是完全一样的,虽然看起来功能很像,但是细微是有差异的 def tf_pixelshuffle(input, upscale_factor):temp []depth upscale_factor *upscale_f…...
sentinel-dashboard-1.8.0.jar开机自启动脚本
启动阿里巴巴的流控组件控制面板需要运行一个jar包,通常需要运行如下命令: 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++堆排序-建堆-插入-删除-排序
本文以大根堆为例,用数组实现,它的nums[0]是数组最大值。 时间复杂度分析: 建堆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...
护眼灯什么价位的好?最具性价比的护眼台灯推荐
到了晚上光线比较弱,这时候就需要开灯,要是孩子需要近距离看字学习等等,给孩子选择的灯具要特别的重视。护眼灯就是目前颇受学生家长青睐的灯具之一,越来越多的人会购买一个护眼灯给自己的孩子让孩子能够在灯光下学习的时候&#…...
vue event bus 事件总线
vue event bus 事件总线 创建 工程: 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…...
深信服云桌面用户忘记密码后的处理
深信服云桌面用户忘记了密码,分两种情况,一个是忘记了登录深信服云桌面的密码,另外一个是忘记了进入操作系统的密码。 一、忘记了登录深信服云桌面的密码 登录虚拟桌面接入管理系统界面,在用户管理中选择用户后,点击后…...
Cocos Creator3.8 实战问题(一)cocos creator prefab 无法显示内容
问题描述: cocos creator prefab 无法显示内容, 或者只显示一部分内容。 creator编辑器中能看见: 预览时,看不见内容: **问题原因:** prefab node 所在的layer,默认是default。 解决方法&…...
朴素贝叶斯深度解码:从原理到深度学习应用
目录 一、简介贝叶斯定理的历史和重要性定义例子 朴素贝叶斯分类器的应用场景定义例子常见应用场景 二、贝叶斯定理基础条件概率定义例子 贝叶斯公式定义例子 三、朴素贝叶斯算法原理基本构成定义例子 分类过程定义例子 不同变体定义例子 四、朴素贝叶斯的种类高斯朴素贝叶斯&a…...
RUST 每日一省:闭包
Rust中的闭包是一种可以存入外层函数中变量或作为参数传递给其他函数的匿名函数。你可以在一个地方创建闭包,然后在不同的上下文环境中调用该闭包来完成运算。和一般的函数不同,闭包可以从定义它的作用域中捕获值。 语法 闭包由“||”和“{}”组合而成。…...
Ubuntu下文件的解压缩操作:常用zip和unzip
Ubuntu下文件的解\压缩 压缩一个文件夹为zip包,加参数-r: zip -r MyWeb.zip MyWeb需要排除目录里某个文件夹?例如我要去掉node_modules,以显著减小压缩包体积,此时该怎么做? zip -r MyWeb.zip ./MyWeb…...
Linux学习第22天:Linux中断驱动开发(一): 突如其来
Linux版本号4.1.15 芯片I.MX6ULL 大叔学Linux 品人间百味 思文短情长 中断作为驱动开发中很重要的一个概念,在实际的项目实践中经常用到。本节的主要内容包括中断简介、硬件原理分析、驱动程序开发及运行测试。其中驱动程…...
IDEA 2019 Springboot 3.1.3 运行异常
项目场景: 在IDEA 2019 中集成Springboot 3.1.3 框架,运行异常。 <?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】飞机大战
代码和图片放在这个地址了: https://gitee.com/r77683962/fighting/tree/master 最新的代码运行,可以有两架飞机,分别通过WASD(方向),F(发子弹);上下左右(控…...
Midjourney 生成油画技巧
基本 prompt oil painting, a cute corgi dog surrounded with colorful flowers技法 Pointillism 点描绘法 笔刷比较细,图像更精细 oil painting, a cute corgi dog surrounded with colorful flowers, pontillismImpasto 厚涂绘法 笔刷比较粗,图像…...
电脑插入多块移动硬盘后经常出现卡顿和蓝屏
当电脑在插入多块移动硬盘后频繁出现卡顿和蓝屏问题时,可能涉及硬件资源冲突、驱动兼容性、供电不足或系统设置等多方面原因。以下是逐步排查和解决方案: 1. 检查电源供电问题 问题原因:多块移动硬盘同时运行可能导致USB接口供电不足&#x…...
页面渲染流程与性能优化
页面渲染流程与性能优化详解(完整版) 一、现代浏览器渲染流程(详细说明) 1. 构建DOM树 浏览器接收到HTML文档后,会逐步解析并构建DOM(Document Object Model)树。具体过程如下: (…...
鱼香ros docker配置镜像报错:https://registry-1.docker.io/v2/
使用鱼香ros一件安装docker时的https://registry-1.docker.io/v2/问题 一键安装指令 wget http://fishros.com/install -O fishros && . fishros出现问题:docker pull 失败 网络不同,需要使用镜像源 按照如下步骤操作 sudo vi /etc/docker/dae…...
Hive 存储格式深度解析:从 TextFile 到 ORC,如何选对数据存储方案?
在大数据处理领域,Hive 作为 Hadoop 生态中重要的数据仓库工具,其存储格式的选择直接影响数据存储成本、查询效率和计算资源消耗。面对 TextFile、SequenceFile、Parquet、RCFile、ORC 等多种存储格式,很多开发者常常陷入选择困境。本文将从底…...
Vite中定义@软链接
在webpack中可以直接通过符号表示src路径,但是vite中默认不可以。 如何实现: vite中提供了resolve.alias:通过别名在指向一个具体的路径 在vite.config.js中 import { join } from pathexport default defineConfig({plugins: [vue()],//…...
C语言中提供的第三方库之哈希表实现
一. 简介 前面一篇文章简单学习了C语言中第三方库(uthash库)提供对哈希表的操作,文章如下: C语言中提供的第三方库uthash常用接口-CSDN博客 本文简单学习一下第三方库 uthash库对哈希表的操作。 二. uthash库哈希表操作示例 u…...
从面试角度回答Android中ContentProvider启动原理
Android中ContentProvider原理的面试角度解析,分为已启动和未启动两种场景: 一、ContentProvider已启动的情况 1. 核心流程 触发条件:当其他组件(如Activity、Service)通过ContentR…...
【1】跨越技术栈鸿沟:字节跳动开源TRAE AI编程IDE的实战体验
2024年初,人工智能编程工具领域发生了一次静默的变革。当字节跳动宣布退出其TRAE项目(一款融合大型语言模型能力的云端AI编程IDE)时,技术社区曾短暂叹息。然而这一退场并非终点——通过开源社区的接力,TRAE在WayToAGI等…...
qt+vs Generated File下的moc_和ui_文件丢失导致 error LNK2001
qt 5.9.7 vs2013 qt add-in 2.3.2 起因是添加一个新的控件类,直接把源文件拖进VS的项目里,然后VS卡住十秒,然后编译就报一堆 error LNK2001 一看项目的Generated Files下的moc_和ui_文件丢失了一部分,导致编译的时候找不到了。因…...
【java】【服务器】线程上下文丢失 是指什么
目录 ■前言 ■正文开始 线程上下文的核心组成部分 为什么会出现上下文丢失? 直观示例说明 为什么上下文如此重要? 解决上下文丢失的关键 总结 ■如果我想在servlet中使用线程,代码应该如何实现 推荐方案:使用 ManagedE…...
