Spring 中循环依赖 三级缓存

 

在Spring框架中,循环依赖是一个常见的问题,它指的是两个或多个Bean之间互相依赖,形成一个闭环,导致无法准确地完成对象的创建和初始化。为了解决这个问题,Spring引入了三级缓存机制。以下是对Spring中循环依赖和三级缓存的详细解释:

一、循环依赖的定义与场景

循环依赖通常发生在以下场景:

  • 直接循环依赖:两个Bean互相依赖,例如Bean A依赖于Bean B,同时Bean B也依赖于Bean A。
  • 间接循环依赖:通过多个Bean形成一个依赖链,最终回到起点,形成一个闭环。

二、三级缓存的定义与作用

Spring的三级缓存主要由三个Map数据结构组成,分别用于存储不同状态的Bean:

  • 一级缓存(singletonObjects):存储已经完全初始化和实例化的Bean对象。这个缓存的目的是确保Bean只初始化一次(是单例的),避免多次实例化相同的Bean对象,提高性能。
  • 二级缓存(earlySingletonObjects):存储尚未完成属性注入和初始化的“半成品”Bean对象。当Spring容器发现两个或多个Bean之间存在循环依赖时,会将这些未完全初始化的对象提前暴露在二级缓存中,以便其他Bean进行引用,确保它们之间的依赖能够被满足。
  • 三级缓存(singletonFactories):存储ObjectFactory类型的代理工厂对象。主要用于处理存在AOP(面向切面编程)时的循环依赖问题。每个Bean都对应一个ObjectFactory对象,通过调用该对象的getObject方法,可以获取到早期暴露出去的Bean。

DefaultSingletonBeanRegistry中定义的这三个map

private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256);private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap(16);private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap(16);

 

public void registerSingleton(String beanName, Object singletonObject) throws IllegalStateException {Assert.notNull(beanName, "Bean name must not be null");Assert.notNull(singletonObject, "Singleton object must not be null");synchronized(this.singletonObjects) {Object oldObject = this.singletonObjects.get(beanName);if (oldObject != null) {throw new IllegalStateException("Could not register object [" + singletonObject + "] under bean name '" + beanName + "': there is already object [" + oldObject + "] bound");} else {this.addSingleton(beanName, singletonObject);}}}protected void addSingleton(String beanName, Object singletonObject) {synchronized(this.singletonObjects) {this.singletonObjects.put(beanName, singletonObject);this.singletonFactories.remove(beanName);this.earlySingletonObjects.remove(beanName);this.registeredSingletons.add(beanName);}}protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {Assert.notNull(singletonFactory, "Singleton factory must not be null");synchronized(this.singletonObjects) {if (!this.singletonObjects.containsKey(beanName)) {this.singletonFactories.put(beanName, singletonFactory);this.earlySingletonObjects.remove(beanName);this.registeredSingletons.add(beanName);}}}@Nullablepublic Object getSingleton(String beanName) {return this.getSingleton(beanName, true);}@Nullableprotected Object getSingleton(String beanName, boolean allowEarlyReference) {Object singletonObject = this.singletonObjects.get(beanName);if (singletonObject == null && this.isSingletonCurrentlyInCreation(beanName)) {singletonObject = this.earlySingletonObjects.get(beanName);if (singletonObject == null && allowEarlyReference) {synchronized(this.singletonObjects) {singletonObject = this.singletonObjects.get(beanName);if (singletonObject == null) {singletonObject = this.earlySingletonObjects.get(beanName);if (singletonObject == null) {ObjectFactory<?> singletonFactory = (ObjectFactory)this.singletonFactories.get(beanName);if (singletonFactory != null) {singletonObject = singletonFactory.getObject();this.earlySingletonObjects.put(beanName, singletonObject);this.singletonFactories.remove(beanName);}}}}}}return singletonObject;}

 

三、循环依赖时的处理流程

当Spring发生循环依赖时,会按照以下流程进行处理:

  1. 遍历待创建的Bean:在容器启动时,Spring会遍历所有需要创建的Bean名称。
  2. Bean的创建与缓存:当Spring尝试获取一个Bean时,会首先在一级缓存中查找。如果找不到,则检查该Bean是否正在创建中。如果是,则会在二级或三级缓存中查找或创建该Bean的实例。
    • 如果Bean是单例且尚未创建完成,会将其BeanFactory存入三级缓存。
  3. 处理依赖注入:在Bean的创建过程中,Spring会处理其依赖注入。如果依赖的Bean不存在于一级或二级缓存中,则会开始创建该依赖Bean的流程。
  4. 循环依赖的解决:当依赖的Bean也需要注入当前正在创建的Bean时,会从三级缓存中获取当前Bean的BeanFactory,并通过BeanFactory的getObject方法获取当前Bean的实例(此时该实例被存入二级缓存,并清除三级缓存中的相关记录)。这样,依赖的Bean就能成功注入当前Bean的属性。
  5. Bean的初始化与缓存更新:当Bean的所有依赖都被注入后,Spring会执行Bean的初始化操作。初始化完成后,该Bean会被存入一级缓存,并清空其在二级和三级缓存中的记录。

 

 

 

四、三级缓存解决循环依赖的示例

假设有两个Bean,A和B,它们之间存在循环依赖:

  • Bean A依赖于Bean B的属性。
  • Bean B依赖于Bean A的属性。

在没有三级缓存的情况下,Spring容器在初始化A和B时会陷入无限循环。但有了三级缓存后,处理流程如下:

  1. Spring开始创建Bean A,发现A依赖于B,于是开始创建B。
  2. 在创建B的过程中,发现B依赖于A。此时,由于A正在创建中,Spring会从三级缓存中获取A的BeanFactory,并通过它创建A的早期暴露实例(存入二级缓存)。
  3. B成功注入A的属性后,完成初始化并存入一级缓存。
  4. 回到A的创建流程,A此时可以成功注入B的属性,完成初始化并存入一级缓存。

通过这个过程,Spring成功解决了A和B之间的循环依赖问题。

 

五、注意事项与限制

  • 单例Bean:Spring解决循环依赖的前提是互相依赖的Bean必须是单例的。对于原型范围的Bean(prototype scope),每次请求都会创建一个新的Bean实例,因此无法解决循环依赖问题。
  • 依赖注入方式:当使用构造函数注入时,如果两个Bean互相依赖对方的构造函数参数,则会产生死锁情况。因此,循环依赖通常发生在Setter注入或字段注入中。

 

 

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

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

相关文章

新能源汽车与公共充电桩布局

近年来,全球范围内对新能源汽车产业的推动力度不断增强,中国新能源汽车市场也呈现蓬勃发展的势头,在政策与市场的共同推动下,新能源汽车销量持续增长。然而,据中国充电联盟数据显示,充电基础设施建设滞后于新能源汽车数量增长的现状导致充电桩供需不平衡,公共充电桩服务空白区域…

【深度学习基础】常用图像卷积核类型

&#x1f308; 个人主页&#xff1a;十二月的猫-CSDN博客 &#x1f525; 系列专栏&#xff1a; &#x1f3c0;深度学习_十二月的猫的博客-CSDN博客 &#x1f4aa;&#x1f3fb; 十二月的寒冬阻挡不了春天的脚步&#xff0c;十二点的黑夜遮蔽不住黎明的曙光 目录 1. 前言 2. 常…

一二三应用开发平台自定义查询设计与实现系列3——通用化重构

通用化重构 前面我们以一个实体为目标对象&#xff0c;完成了功能开发与调试。 在此基础上&#xff0c;我们对功能进行重构&#xff0c;使其成为平台的标准化、通用化的功能。 前端重构 首先&#xff0c;先把自定义组件挪到了平台公共组件目录下&#xff0c;如下&#xff1…

RabbitMQ交换机类型

RabbitMQ交换机类型 1、RabbitMQ工作模型2、RabbitMQ交换机类型2.1、Fanout Exchange&#xff08;扇形&#xff09;2.1.1、介绍2.1.2、示例2.1.2.1、生产者2.1.2.2、消费者2.1.2.3、测试 2.2、Direct Exchange&#xff08;直连&#xff09;2.2.1、介绍2.2.2、示例2.2.2.1、生产…

qt QMenuBar详解

1、概述 QMenuBar是Qt框架中用于创建菜单栏的类&#xff0c;它继承自QWidget。QMenuBar通常位于QMainWindow对象的标题栏下方&#xff0c;用于组织和管理多个QMenu&#xff08;菜单&#xff09;和QAction&#xff08;动作&#xff09;。菜单栏提供了一个水平排列的容器&#x…

数据转换 | Matlab基于SP符号递归图(Symbolic recurrence plots)一维数据转二维图像方法

目录 基本介绍程序设计参考资料获取方式 基本介绍 Matlab基于SP符号递归图&#xff08;Symbolic recurrence plots&#xff09;一维数据转二维图像方法 符号递归图(Symbolic recurrence plots)是一种一维时间序列转图像的技术&#xff0c;可用于平稳和非平稳数据集;对噪声具有…

特殊矩阵的压缩存储

一维数组的存储结构 ElemType arr[10]; 各数组元素大小相同&#xff0c;且物理上连续存放。 数组元素a[i]的存放地址 LOC i * sizeof(ElemType)。&#xff08;LOC为起始地址&#xff09; 二维数组的存储结构 ElemType b[2][4];二维数组也具有随机存取的特性&#xff08;需…

MySQL utf8mb3 和 utf8mb4引发的问题

问题描述 Cause: java.sql.SQLException: Incorrect string value: \xF4\x8F\xBB\xBF-b... for column sddd_aaa_ark at row 1 sddd_aaa_ark 存储中文字符时&#xff0c;出现上述问题 原因分析 sddd_aaa_ark在数据库中结构是 utf8字符的最大字节数是3 byte&#xff0c;但是某些…

RK3568开发板Openwrt文件系统构建

RK3568开发板Openwrt文件系统构建 iTOP-RK3568开发板使用教程更新&#xff0c;后续资料会不断更新&#xff0c;不断完善&#xff0c;帮助用户快速入门&#xff0c;大大提升研发速度。 本次更新内容为《iTOP-3568开发板文件系统构建手册》&#xff0c;对Openwrt文件系统的编译…

Linux之crontab使用

一&#xff0c;查看cron是否已经在运行 查看crontab的运行状态 sudo service cron statussystemctl status cron 开启crontab: sudo service cron startsudo service cron restart 二&#xff0c;编辑cron定时任务 crontab -e加入你自己的命令&#xff0c;定时跑脚本&a…

OpenEuler 使用ffmpeg x11grab捕获屏幕流,rtsp推流,并用vlc播放

环境准备 安装x11grab(用于捕获屏幕流)和libx264(用于编码) # 基础开发环境&x11grab sudo dnf install -y \autoconf \automake \bzip2 \bzip2-devel \cmake \freetype-devel \gcc \gcc-c \git \libtool \make \mercurial \pkgconfig \zlib-devel \libX11-devel \libXext…

矩阵的奇异值分解SVD

为了论述矩阵的奇异值与奇异值分解!需要下面的结论!

H5开发指南|掌握核心技术,玩转私域营销利器

随着互联网技术的不断发展和用户需求的日益增长&#xff0c;H5页面逐渐成为了企业和个人展示信息、吸引用户关注的重要手段。具有跨平台兼容性强、网页链接分享、更新迭代方便快捷、低开发成本、可搜索和优化、数据分析与追踪、灵活性与扩展性以及无需下载安装等特点。不仅可以…

Ubuntu Linux

背景 Ubuntu起源于南非&#xff0c;其名称“Ubuntu”来源于非洲南部祖鲁语或豪萨语&#xff0c;意为“人性”、“我的存在是因为大家的存在”&#xff0c;这体现了非洲传统的一种价值观。Ubuntu由南非计算机科学家马克沙特尔沃斯&#xff08;Mark Shuttleworth&#xff09;创办…

你适合哪种tiktok广告账户类型?

TikTok在广告营销方面的分类体系极为详尽。在开设广告账户时&#xff0c;根据不同的海外市场和商品类型&#xff0c;TikTok会有各自的开户标准。此外&#xff0c;广告主所开设的TikTok广告账户类型会直接影响其可投放的广告类型。在广告出价方面&#xff0c;广告主的营销目标不…

平衡者:陈欣的宇宙使命

第一章 异象初现 2145年&#xff0c;地球已经不再是人类唯一的家园。随着科技的飞速发展&#xff0c;人类在银河系内建立了多个殖民星球。然而&#xff0c;这些新世界的繁荣背后隐藏着一个巨大的危机——各个星球之间的资源分配不均&#xff0c;导致了严重的社会动荡和冲突。 …

《AI产品经理手册》——解锁AI时代的商业密钥

在当今这个日新月异的AI时代&#xff0c;每一位产品经理都面临着前所未有的挑战与机遇&#xff0c;唯有紧跟时代潮流&#xff0c;深入掌握AI技术的精髓&#xff0c;才能在激烈的市场竞争中独占鳌头。《AI产品经理手册》正是这样一部为AI产品经理量身定制的实战宝典&#xff0c;…

React第十三章(useTransition)

useTransition useTransition 是 React 18 中引入的一个 Hook&#xff0c;用于管理 UI 中的过渡状态&#xff0c;特别是在处理长时间运行的状态更新时。它允许你将某些更新标记为“过渡”状态&#xff0c;这样 React 可以优先处理更重要的更新&#xff0c;比如用户输入&#x…

使用wordcloud与jieba库制作词云图

目录 一、WordCloud库 例子&#xff1a; 结果&#xff1a; 二、Jieba库 两个基本方法 jieba.cut() jieba.cut_for_serch() 关键字提取&#xff1a; jieba.analyse包 extract_tags() 一、WordCloud库 词云图&#xff0c;以视觉效果提现关键词&#xff0c;可以过滤文本…

2024年云手机推荐榜单:高性能云手机推荐

无论是手游玩家、APP测试人员&#xff0c;还是数字营销工作者&#xff0c;云手机都为他们带来了极大的便利。本文将为大家推荐几款在市场上表现优异的云手机&#xff0c;希望这篇推荐指南可以帮助大家找到最适合自己的云手机&#xff01; 1. OgPhone云手机 OgPhone云手机是一款…