Spring解决循环依赖
Spring框架为了解决循环依赖问题,设计了一套三级缓存机制:
- 一级缓存singletonObjects:这个是最常规的缓存,用于存放完成初始化好的bean,如果某个bean已经在这个缓存了直接返回。
- 二级缓存earlySigletonObjects:这个用于存放早期暴露出来的bean,就是那些创建出来还没有初始化好的bean,这样做的目的就是为了bean创建过程中能提前暴露出来,方便解决循环依赖的问题。
- 三级缓存 singletonFactories:这个缓存存放的是bean的工厂对象,这个工厂对象负责bean的实例,当一个bean创建时,它的工厂对象会被放入缓存中。
Spring解决循环依赖

三级缓存
-
singletonObjects:用于存放完全初始化好的 bean,从该缓存中取出的 bean 可以直接使用
-
earlySingletonObjects:提前曝光的单例对象的cache,存放原始的 bean 对象(尚未填充属性),用于解决循环依赖
-
singletonFactories:单例对象工厂的cache,存放 bean 工厂对象,用于解决循环依赖
整体流程
-
首先A完成初始化第一步先将自己提前曝光出来(通过ObjectFactory将自己提前曝光出来),在初始化的时候,发现自己需要依赖B,就开始尝试get(B),这时候发现B还没有创建;
-
B走创建流程,在B初始化的时候依赖C,C还没有创建出来;
-
C开始初始化,在C初始化的时候发现自己依赖A,于是尝试get(A),这时候由于A已经添加到缓存中了(一般都是添加到三级缓存中singletonFactory),通过ObjectFactory提前曝光,所以可以通过ObjectFactory#getObject()获取到A对象。C拿着A对象后顺利初始化,然后自己添加到一级缓存中;
-
回到B,B也拿到C对象,完成初始化,A可以顺利拿到B,这里整个链路已经完成初始化过程了。
关键字:三级缓存,提前曝光
再来一个例子
假设现在有两个bean,一个A,一个B;
Spring容器开始创建A对象,会先去一级缓存中查看是否有BeanA的实例,如果没有就会创建一个A的实例,并将其工厂对象放入三级缓存中,然后BeanA 的创建因为需要注入B而被挂起,Spring开始创建BeanB对象。
BeanB同样回去一级缓存中查找是否存在B实例,由于还没有创建,Spring会将bean B的半成品放入二级缓存,继续创建买这个B需要依赖A,由于A的工厂对象已经放在三级缓存中了,spring可以直接获取三级对象中的beanA的工厂对象,通过它来创建beanA的实例。
这样,即使两个 beans 相互依赖,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需要三级缓存来确保在复杂情况下依然能够正常工作。
相关文章:
Spring解决循环依赖
Spring框架为了解决循环依赖问题,设计了一套三级缓存机制: 一级缓存singletonObjects:这个是最常规的缓存,用于存放完成初始化好的bean,如果某个bean已经在这个缓存了直接返回。二级缓存earlySigletonObjects:这个用于存放早期暴…...
RUST运算符重载
在 Rust 中,可以使用特征(traits)来实现运算符重载。运算符重载是通过实现相应的运算符特征(如 Add、Sub、Mul 等)来完成的。这些特征定义在 std::ops 模块中。下面是一个简单的示例,展示如何为一个自定义结…...
描述一下 Array.forEach() 循环和 Array.map() 方法之间的主要区别
Array.forEach() 和 Array.map() 都是 JavaScript 数组中常用的方法,但它们之间有一些重要的区别: 返回值:forEach():没有返回值,它只是对数组中的每个元素执行提供的函数。map():返回一个新的数组,其元素是通过对原数组的每个元素执行提供的函数后的结…...
在GEE中显示矢量或栅格数据的边界(包含样式设计)
需要保证最后显示的数据是一个 FeatureCollection 对象。 如果数据是一个 Geometry 或 Image,我们也可以使用 style 方法来设置样式并将其添加到地图上。以下是针对不同类型对象的处理方式: 1 Geometry对象 如果 table 是一个 Geometry 对象ÿ…...
django使用fetch上传文件
在上一篇文章中,我包装了fetch方法,使其携带cookie。但是之前fetch传递的是json数据,现在有了一个上传文件的需求,因此需要进行修改: const sendRequest (url, method, data) > {const csrftoken Cookies.get(cs…...
linux安装docker步骤
步骤1 脚本自动安装 curl -fsSL https://get.docker.com -o get-docker.sh sudo sh get-docker.sh 步骤2 启动Docker服务: sudo systemctl start docker 步骤3 验证Docker是否正确安装并运行一个容器: sudo docker run hello-world 安装正确,则可拉取镜像&am…...
Unity DOTS技术(一)简介
文章目录 一.概述二.将会介绍的内容三.DOTS技术与传统方式的不同传统问题DOTS技术 四.插件安装 一.概述 传统的游戏开发中,如果有成千上万的物体在场景中运动,那么你一定会认为是疯了.但有了Dost技术这一些都将变成可能.如图场景中有10000个物体在同时运动,帧率即能保持在60Fp…...
深度解读ChatGPT基本原理
一、基本原理 ChatGPT是一个基于深度学习的自然语言生成模型,使用了类似于GPT(Generative Pre-trained Transformer)的架构。GPT是由OpenAI开发的一种语言模型,能够生成与输入文本相关的连续性文本。 ChatGPT的基本原理是先使用…...
python实现——分类类型数据挖掘任务(图形识别分类任务)
分类类型数据挖掘任务 基于卷积神经网络(CNN)的岩石图像分类。有一岩石图片数据集,共300张岩石图片,图片尺寸224x224。岩石种类有砾岩(Conglomerate)、安山岩(Andesite)、花岗岩&am…...
【安卓跨进程通信IPC】-- Binder
目录 BinderBinder是什么?进程空间分配进程隔离Binder跨进程通信机制模型优点AIDL常见面试题 Binder 夯实基础之超详解Android Binder的工作方式与原理以及aidl示例代码 比较详细的介绍:Android跨进程通信:图文详解 Binder机制 原理 操作系统…...
大数据之Schedule调度错误(一)
当我们在利用ooize发起整个任务的调度过程中,如果多个调度任务同时运行并且多个调度任务操作了相同的表,那么就会出现如下的错误关系: Invalid path hdfs://iZh5w01l7f8lnog055cpXXX:8000/user/admin/xxx: No files matching path hdfs://iZh5w01l7f8lnog055cpXXX:8000/user/ad…...
DiffIR论文阅读笔记
ICCV2023的一篇用diffusion模型做Image Restoration的论文,一作是清华的教授,还在NIPS2023上一作发表了Hierarchical Integration Diffusion Model for Realistic Image Deblurring,作者里甚至有Luc Van Gool大佬。模型分三个部分,…...
prometheus+alertmanager+webhook钉钉机器人告警
版本:centos7.9 python3.9.5 alertmanager0.25.0 prometheus2.46.0 安装alertmanager prometheus 配置webhook # 解压: tar -xvf alertmanager-0.25.0.linux-amd64.tar.gz tar -xvf prometheus-2.46.0.linux-amd64.tar.gz mv alertmanager-0.25.0.linu…...
ctfshow 年CTF web
除夕 Notice: Undefined index: year in /var/www/html/index.php on line 16 <?phpinclude "flag.php";$year $_GET[year];if($year2022 && $year1!2023){echo $flag; }else{highlight_file(__FILE__); } 弱比较绕过很简单,连函数都没有直…...
原型链、闭包、手写一个闭包函数、 闭包有哪些优缺点、原型链继承
什么是原型链? 原型链是一种查找规则 为对象成员查找机制提供一个方向 因为构造函数的 prototype 和其实例的 __ proto __ 都是指向原型对象的 所以可以通过__proto__ 查找当前的原型对象有没有该属性, 没有就找原型的原型, 依次类推一直找到Object( null ) 为…...
linux中SSH_ASKPASS全局变量的作用
在工作中遇到一段代码,通过SSH_ASKPASS全局变量实现了ssh登录远程IP时的密码输入,chatgpt搜索了一下,其解释大致如下所示: SSH_ASKPASS 是一个环境变量,它在 SSH 客户端需要用户输入密码时起作用。当 SSH 客户端检测到…...
9 -力扣高频 SQL 50 题(基础版)
9 - 上升的温度 -- 找出与之前(昨天的)日期相比温度更高的所有日期的 id -- DATEDIFF(2007-12-31,2007-12-30); # 1 -- DATEDIFF(2010-12-30,2010-12-31); # -1select w1.id from Weather w1, Weather w2 wheredatediff(w1.recordDate,w2.recordDat…...
TCP的重传机制
TCP 是一个可靠的传输协议,解决了IP层的丢包、乱序、重复等问题。这其中,TCP的重传机制起到重要的作用。 序列号和确认号 之前我们在讲解TCP三次握手时,提到过TCP包头结构,其中有序列号和确认号, 而TCP 实现可靠传输…...
pg 数据库,获取时间字段值的具体小时,赋值给其他字段
目录 1 问题2 实现 1 问题 pg 数据库,有一个表,其中有2个字段 一个是时间字段obstime ,一个是时次ltime字段,int 类型,现在这个表里面是obstime 里面有数据,ltime字段 没有数据,现在就是批量获…...
做视频号小店什么类目最容易爆单?其实,弄懂这三点就会选品了
大家好,我是电商花花。 我们做视频号小店做什么类目最容易爆单? 其实任何类目都有属于自己的受众人群和客户,都非常容易爆单,我们想要爆单,就要选对类目,选对产品。 视频号上所有的类目基本上可以分为标…...
业务系统对接大模型的基础方案:架构设计与关键步骤
业务系统对接大模型:架构设计与关键步骤 在当今数字化转型的浪潮中,大语言模型(LLM)已成为企业提升业务效率和创新能力的关键技术之一。将大模型集成到业务系统中,不仅可以优化用户体验,还能为业务决策提供…...
Java 语言特性(面试系列2)
一、SQL 基础 1. 复杂查询 (1)连接查询(JOIN) 内连接(INNER JOIN):返回两表匹配的记录。 SELECT e.name, d.dept_name FROM employees e INNER JOIN departments d ON e.dept_id d.dept_id; 左…...
阿里云ACP云计算备考笔记 (5)——弹性伸缩
目录 第一章 概述 第二章 弹性伸缩简介 1、弹性伸缩 2、垂直伸缩 3、优势 4、应用场景 ① 无规律的业务量波动 ② 有规律的业务量波动 ③ 无明显业务量波动 ④ 混合型业务 ⑤ 消息通知 ⑥ 生命周期挂钩 ⑦ 自定义方式 ⑧ 滚的升级 5、使用限制 第三章 主要定义 …...
通过Wrangler CLI在worker中创建数据库和表
官方使用文档:Getting started Cloudflare D1 docs 创建数据库 在命令行中执行完成之后,会在本地和远程创建数据库: npx wranglerlatest d1 create prod-d1-tutorial 在cf中就可以看到数据库: 现在,您的Cloudfla…...
关于nvm与node.js
1 安装nvm 安装过程中手动修改 nvm的安装路径, 以及修改 通过nvm安装node后正在使用的node的存放目录【这句话可能难以理解,但接着往下看你就了然了】 2 修改nvm中settings.txt文件配置 nvm安装成功后,通常在该文件中会出现以下配置&…...
用docker来安装部署freeswitch记录
今天刚才测试一个callcenter的项目,所以尝试安装freeswitch 1、使用轩辕镜像 - 中国开发者首选的专业 Docker 镜像加速服务平台 编辑下面/etc/docker/daemon.json文件为 {"registry-mirrors": ["https://docker.xuanyuan.me"] }同时可以进入轩…...
Linux nano命令的基本使用
参考资料 GNU nanoを使いこなすnano基础 目录 一. 简介二. 文件打开2.1 普通方式打开文件2.2 只读方式打开文件 三. 文件查看3.1 打开文件时,显示行号3.2 翻页查看 四. 文件编辑4.1 Ctrl K 复制 和 Ctrl U 粘贴4.2 Alt/Esc U 撤回 五. 文件保存与退出5.1 Ctrl …...
华为OD最新机试真题-数组组成的最小数字-OD统一考试(B卷)
题目描述 给定一个整型数组,请从该数组中选择3个元素 组成最小数字并输出 (如果数组长度小于3,则选择数组中所有元素来组成最小数字)。 输入描述 行用半角逗号分割的字符串记录的整型数组,0<数组长度<= 100,0<整数的取值范围<= 10000。 输出描述 由3个元素组成…...
【堆垛策略】设计方法
堆垛策略的设计是积木堆叠系统的核心,直接影响堆叠的稳定性、效率和容错能力。以下是分层次的堆垛策略设计方法,涵盖基础规则、优化算法和容错机制: 1. 基础堆垛规则 (1) 物理稳定性优先 重心原则: 大尺寸/重量积木在下…...
【Kafka】Kafka从入门到实战:构建高吞吐量分布式消息系统
Kafka从入门到实战:构建高吞吐量分布式消息系统 一、Kafka概述 Apache Kafka是一个分布式流处理平台,最初由LinkedIn开发,后成为Apache顶级项目。它被设计用于高吞吐量、低延迟的消息处理,能够处理来自多个生产者的海量数据,并将这些数据实时传递给消费者。 Kafka核心特…...
