Spring 的三级缓存机制

Spring 的三级缓存机制

Spring 的三级缓存机制是解决循环依赖的关键

Spring 框架为了解决循环依赖问题,设计了一套三级缓存机制。这三级缓存分别是:

  1. 一级缓存 singletonObjects:这是最常规的缓存,用于存放完全初始化好的 bean。如果某个 bean 已经在这个缓存中,则直接返回这个 bean 的实例。
  2. 二级缓存 earlySingletonObjects:这个缓存用于存放早期暴露出来的 bean,也就是那些已经创建但尚未完成初始化(如属性填充和初始化方法调用)的 bean。这样做的目的是为了在 bean 的创建过程中就能提前暴露出来,以便于解决循环依赖的问题。
  3. 三级缓存 singletonFactories:这个缓存存放的是 bean 的工厂对象,这些工厂对象负责生成 bean 的实例。当一个 bean 被创建时,它的工厂对象会被放入这个缓存中。

通过这三级缓存,Spring 能够在 bean 的创建过程中就解决循环依赖的问题。具体来说,当 AOP 代理对象需要引用其他 bean 时,可以通过提前暴露的二级缓存来获取尚未完全初始化的 bean,从而打破循环依赖的僵局。

以下是一个简单的例子来说明这个过程:

假设有两个 bean,A 和 B,它们相互依赖。当 Spring 容器开始创建 bean A 时,它会首先检查一级缓存中是否已经有了 bean A 的实例。如果没有,它会创建一个 bean A 的实例,并将其工厂对象放入三级缓存中。然后,bean A 的创建过程会因为需要注入 bean B 而被挂起,Spring 会转而去创建 bean B。

在创建 bean B 的过程中,同样会检查一级缓存中是否已经有了 bean B 的实例。由于还没有创建,Spring 会将 bean B 的半成品(即尚未完成初始化的实例)放入二级缓存中,并继续创建过程。这时,bean B 也需要依赖 bean A,但由于 bean A 的工厂对象已经在三级缓存中,Spring 可以直接从三级缓存中获取到 bean A 的工厂对象,并通过它来创建 bean A 的实例。

这样,即使两个 beans 相互依赖,Spring 也能够通过三级缓存机制成功地创建它们,解决了循环依赖的问题。

在这里插入图片描述

参考文章——Spring中的“三级缓存”

参考文章——Spring面试题之循环依赖与三级缓存

为什么 Spring 只有二级缓存不行

Spring只有二级缓存确实无法完全解决循环依赖问题,尤其是在涉及到AOP代理时

二级缓存earlySingletonObjects用于存储半成品的Bean实例,即那些已经被实例化但尚未完成初始化(如属性填充和方法调用)的Bean。这个缓存允许Spring在Bean的创建过程中就能提前暴露出来,以便于解决循环依赖的问题。然而,如果只有二级缓存,当涉及到AOP代理时,问题就来了。

AOP代理的生成是在Bean的初始化阶段完成的,这意味着在Bean的所有属性都被设置之后。如果一个Bean需要被代理,那么在代理之前,Spring会尽量从缓存中获取到原始的Bean实例,以避免在代理过程中出现循环引用的问题。但是,如果只有二级缓存,那么在Bean初始化之前,我们无法从缓存中获取到代理对象,因为二级缓存中存储的是尚未初始化的Bean实例,而不是代理对象。

举个例子,假设有两个Bean,A和B,它们相互依赖,并且A需要被AOP代理。在Spring的创建过程中,首先会创建A的实例并将其放入二级缓存中。然后,当尝试创建B并注入A时,会发现A还没有完成初始化,因此无法生成A的代理对象。这样就会导致循环依赖的问题无法被解决。

而三级缓存中的singletonFactories存储的是Bean的工厂对象,可以在Bean初始化之前就生成代理对象,并将其放入一级缓存singletonObjects中。这样,即使Bean之间存在循环依赖,Spring也能够通过三级缓存机制成功地创建它们,解决了循环依赖的问题。

总的来说,三级缓存机制是Spring为了在保持设计原则的同时,解决循环依赖和AOP代理的问题而设计的。二级缓存虽然可以解决部分循环依赖的问题,但在面对AOP代理时就显得力不从心了。因此,Spring需要三级缓存来确保在复杂情况下依然能够正常工作。

让我们通过一个具体的例子来理解为什么仅有二级缓存无法解决涉及AOP代理的循环依赖问题。

假设我们有两个Bean,ServiceAServiceB,它们相互依赖,并且ServiceA需要被AOP代理(例如,为了实现事务管理)。

  1. 创建ServiceA实例:Spring首先尝试创建ServiceA的实例。在实例化过程中,ServiceA尚未完成初始化(例如,还未填充属性,未调用初始化方法等),但它的半成品实例被放入了二级缓存earlySingletonObjects

  2. 创建ServiceB实例:接着,Spring尝试创建ServiceB的实例,并试图注入ServiceA。由于ServiceA的完整初始化尚未完成,因此无法生成其AOP代理。

  3. AOP代理问题:在Spring中,AOP代理是在Bean的初始化阶段完成的,这意味着所有的属性填充和方法调用之后。如果ServiceA需要被代理,那么在代理之前,Spring会尝试从缓存中获取到原始的ServiceA实例,以避免在代理过程中出现循环引用的问题。但是,如果只有二级缓存,那么在ServiceA初始化之前,我们无法从缓存中获取到代理对象,因为二级缓存中存储的是尚未初始化的ServiceA实例,而不是代理对象。

  4. 循环依赖:由于ServiceB需要注入ServiceA,而ServiceA又需要被AOP代理,这就形成了一个循环依赖。如果没有三级缓存,Spring就无法在ServiceA完全初始化之前创建一个代理实例来解决这个循环依赖。

现在,让我们看看三级缓存是如何解决这个问题的:

  • 三级缓存singletonFactories:在创建ServiceA的过程中,它的工厂对象会被放入三级缓存singletonFactories。这个工厂对象负责生成ServiceA的实例。

  • 解决循环依赖:当创建ServiceB并尝试注入ServiceA时,Spring会检查三级缓存中是否存在ServiceA的工厂对象。如果存在,Spring使用该工厂对象来创建ServiceA的代理实例,并将其放入一级缓存singletonObjects中。这样,即使ServiceAServiceB相互依赖,Spring也能够通过三级缓存机制成功地创建它们,解决了循环依赖的问题。

总结来说,二级缓存能够解决一些循环依赖问题,但在涉及AOP代理的情况下,它无法提供一个尚未初始化的Bean的代理实例。三级缓存通过引入工厂对象来提前创建代理实例,从而解决了这个问题。

创建ServiceB实例:接着,Spring 尝试创建 ServiceB 的实例,并试图注入 ServiceA。由于 ServiceA 的完整初始化尚未完成,因此无法生成其 AOP 代理。 这里为什么要生成 ServiceA 的 AOP 代理,是不是 ServiceB 要注入的是 ServiceA 的代理对象

是的,ServiceB要注入的是ServiceA的代理对象。在Spring中,如果一个Bean被AOP代理,那么其他Bean注入的是这个Bean的代理对象,而不是原始的Bean实例。这样做的目的是为了让代理对象能够拦截对原始Bean方法的调用,以便在调用前后执行额外的操作,如日志记录、事务管理等。

在创建ServiceB实例时,如果需要注入ServiceA,而ServiceA尚未完成初始化,就无法生成其AOP代理。这是因为AOP代理是在Bean的初始化阶段完成的,这意味着所有的属性填充和方法调用之后。如果ServiceA需要被代理,那么在代理之前,Spring会尝试从缓存中获取到原始的ServiceA实例,以避免在代理过程中出现循环引用的问题。

因此,生成ServiceA的AOP代理是为了确保在Bean的初始化阶段能够生成代理对象,并将其注入到其他Bean中,从而实现横切关注点的模块化。这样,开发人员可以专注于核心业务逻辑的开发,而不必关心那些横切关注点的实现细节。

三级缓存singletonFactories 既可以创建代理bean也可以创建非代理bean

是的,三级缓存singletonFactories可以用于创建代理bean和非代理bean。

在Spring中,singletonFactories是一个Map类型的缓存,它存储了Bean的工厂对象。这些工厂对象负责生成Bean的实例。当需要创建一个Bean时,Spring会首先检查一级缓存singletonObjects中是否已经存在该Bean的实例。如果不存在,则继续检查二级缓存earlySingletonObjects中是否存在半成品的Bean实例。如果仍然不存在,Spring会从三级缓存singletonFactories中获取到对应Bean的工厂对象,并使用该工厂对象来创建Bean的实例。

对于非代理Bean,singletonFactories中的工厂对象直接创建Bean的实例并将其放入一级缓存singletonObjects中。而对于需要被AOP代理的Bean,singletonFactories中的工厂对象会先创建一个原始的Bean实例,然后通过AOP代理来生成代理对象,并将代理对象放入一级缓存singletonObjects中。

因此,三级缓存singletonFactories既可以创建代理bean,也可以创建非代理bean,根据具体的需求和配置来决定是否需要进行AOP代理。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/264762.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【服务器数据恢复】FreeNAS+ESXi虚拟机数据恢复案例

服务器数据恢复环境: 一台服务器通过FreeNAS(本案例使用的是UFS2文件系统)实现iSCSI存储,整个UFS2文件系统作为一个文件挂载到ESXi虚拟化系统(安装在另外2台服务器上)上。该虚拟化系统一共有5台虚拟机&…

什么是高可用架构

一、什么是高可用 在运维中,经常听到高可用,那么什么是高可用架构呢?通俗点讲,高可用就是在服务故障,节点宕机的情况下,业务能够保证不中断,服务正常运行。 举个例子,支付宝&#…

开源大模型LLM大爆发,数据竞赛已开启!如何使用FuseLLM实现大语言模型的知识融合?

开源大模型LLM大爆发,数据竞赛已开启!如何使用FuseLLM实现大语言模型的知识融合? 现在大多数人都知道LLM是什么,以及可以做什么。 人们讨论着它的优缺点,畅想着它的未来, 向往着真正的AGI,又有…

弱结构化日志 Flink SQL 怎么写?SLS SPL 来帮忙

作者:潘伟龙(豁朗) 背景 日志服务 SLS 是云原生观测与分析平台,为 Log、Metric、Trace 等数据提供大规模、低成本、实时的平台化服务,基于日志服务的便捷的数据接入能力,可以将系统日志、业务日志等接入 …

MS2403隔离Σ-Δ调制器

产品简述 MS2403 是一款二阶 Σ-Δ 调制器,集成片上数字隔离器,能 将模拟输入信号转换为高速 1 位码流。调制器对输入信号连续 采样,无需外部采样保持电路。模拟信号输入满量程为 320 mV ,转换后的数字码流的最高数据速率为 2…

STM32系列芯片的命名规则(STM32学习之路)

STM32系列芯片命名规则 STM32F103C8T6 1.产品系列: STM32代表ST品牌Cortex-Mx系列内核(ARM)的32位MCU 2.产品类型: F :通用快闪(FlashMemory) L:低电压(1.65&#xf…

IO进程线程day8作业

信号灯集二次函数封装 sem.c #include<myhead.h>union semun {int val; /* Value for SETVAL */struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */unsigned short *array; /* Array for GETALL, SETALL */struct seminfo *__buf; /* B…

Educational Codeforces Round 162 (Rated for Div. 2)(A~D)

A. Moving Chips 分析&#xff1a;先找出区间[l,r]&#xff0c;a[l]为1第一次出现&#xff0c;a[r]为1最后一次出现&#xff0c;[l,r]以外的区间不用管。 所以我们要将[l,r]中所有1的区域块练成一块。 经过简单的样例分析发现&#xff0c;假设1的区域t1和区域t2间有x个0&…

springboot+vue实现微信公众号扫码登录

通常在个人网站中&#xff0c;都会有各种第三方登录&#xff0c;其中微信登录需要认证才能使用&#xff0c;导致个人开发者不能进行使用此功能&#xff0c;但是我们可以使用微信公众号回复特定验证码来进行登录操作。 微信关键词处理 微信公众号关键词自动回复&#xff0c;具体…

第3.5章:StarRocks数据导入——Broker Load

注&#xff1a;本篇文章阐述的是StarRocks-3.2版本的Broker Load导入机制 一、概述 Broker Load导入方式支持从HDFS类的外部存储系统&#xff08;例如&#xff1a;HDFS、阿里OSS、腾讯COS、华为云OBS等&#xff09;&#xff0c;支持Parquet、ORC、CSV、及 JSON 四种文件格式&a…

c#程序,oracle使用Devart驱动解决第第三方库是us7ascii,数据乱码的问题

最近做项目&#xff0c;要跟对方系统的库进行读写&#xff0c;结果发现对方采用的是oracle的us7ascii编码&#xff0c;我们系统默认采用的是ZHS16GBK&#xff0c;导致我们客户端读取和写入对方库的数据都是乱码&#xff0c;搜索网上&#xff0c;发现需要采用独立的oracle驱动去…

【GPTs分享】GPTs分享之Write For Me

Write For Me 是一个专门定制的GPT版本&#xff0c;旨在为用户提供高质量的文本内容创作服务。它适用于各种写作需求&#xff0c;从商业计划、学术文章到创意故事等。下面是从简介、主要功能、使用案例、优点和局限性几个方面对Write For Me 的详细介绍。 简介 Write For Me …

【初始RabbitMQ】高级发布确认的实现

在生产环境中由于一些不明原因&#xff0c;导致 rabbitmq 重启&#xff0c;在 RabbitMQ 重启期间生产者消息投递失败&#xff0c; 导致消息丢失&#xff0c;需要手动处理和恢复。于是&#xff0c;我们开始思考&#xff0c;如何才能进行 RabbitMQ 的消息可靠投递呢&#xff1f; …

常用实验室器皿耐硝酸盐酸进口PFA材质容量瓶螺纹盖密封效果好

PFA容量瓶规格参考&#xff1a;10ml、25ml、50ml、100ml、250ml、500ml、1000ml。 别名可溶性聚四氟乙烯容量瓶、特氟龙容量瓶。常用于ICP-MS、ICP-OES等痕量分析以及同位素分析等实验&#xff0c;也可在地质、电子化学品、半导体分析测试、疾控中心、制药厂、环境检测中心等机…

蜣螂优化算法DBO求解不闭合SD-MTSP,可以修改旅行商个数及起点(提供MATLAB代码)

一、蜣螂优化算法&#xff08;Dung beetle optimizer&#xff0c;DBO&#xff09; 蜣螂优化算法&#xff08;Dung beetle optimizer&#xff0c;DBO&#xff09;由Jiankai Xue和Bo Shen于2022年提出&#xff0c;该算法主要受蜣螂的滚球、跳舞、觅食、偷窃和繁殖行为的启发所得…

YOLOv9-Openvino和ONNXRuntime推理【CPU】

1 环境&#xff1a; CPU&#xff1a;i5-12500 Python&#xff1a;3.8.18 2 安装Openvino和ONNXRuntime 2.1 Openvino简介 Openvino是由Intel开发的专门用于优化和部署人工智能推理的半开源的工具包&#xff0c;主要用于对深度推理做优化。 Openvino内部集成了Opencv、Tens…

开源大语言模型作为 LangChain 智能体

概要 开源大型语言模型 (LLMs) 现已达到一种性能水平&#xff0c;使它们适合作为推动智能体工作流的推理引擎: Mixtral 甚至在我们的基准测试中 超过了 GPT-3.5&#xff0c;并且通过微调&#xff0c;其性能可以轻易的得到进一步增强。 引言 针对 因果语言建模 训练的大型语言模…

使用 npm/yarn 等命令的时候会,为什么会发生 Error: certificate has expired

缘起 昨天&#xff0c;我写了一篇文章&#xff0c;介绍如何使用项目模板&#xff0c;构建一个 Electron 项目的脚手架&#xff0c;我发现我自己在本地无法运行成功&#xff0c;出现了错误。 ✖ Failed to install modules: ["electron-forge/plugin-vite^7.2.0",&qu…

golang学习3,golang 项目中配置gin的web框架

1.go 初始化 mod文件 go mod init gin-ranking 2.gin的crm框架 go get -u github.com/gin-gonic/gin 3.go.mod爆红解决

C# 通过共享内存调用C++ 算法

需求&#xff1a; C#程序调用 C开发的dll. 一种C# 程序调用c 算法方案_算法怎么被c#调用-CSDN博客 上回书说到&#xff0c;将c算法封装为dll 插件&#xff0c;c加载后&#xff0c;暴露C风格接口&#xff0c;然后供C#调用。但是这样有几个问题&#xff1a; 1&#xff0c;一是…