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

Spring中Bean的循环依赖

目录

定义:

循环依赖的后果:

一:三级缓存

1、大概的思路:

注意:

2、执行过程:

A半完成:

B完成:

A完成:

注:

二:@Lazy


定义:

一个或多个Bean之间存在相互调用关系就会产生循环依赖。即:A调用B,B调用C,C调用A,这样会导致创建对象时依赖链过长,栈溢出。

循环依赖的后果:

环依赖可能会导致程序出现各种问题,比如编译错误、运行时错误、死锁等。因此,避免循环依赖是编写高质量软件的重要方面之一。

Spring为了解决循环依赖问题,引入了三级缓存,Bean的生命周期中我们可以知道Bean在实例化的时候会通过Bean的构造函数来实例化Bean和进行属性填充,所以就要在这一步之前对Bean进行一些操作。

一:三级缓存

解读源码流程之前,spring 内部的三级缓存逻辑必须了解,要不然后面看代码会蒙圈。

第一级缓存 :singletonObjects,用于保存实例化、注入、初始化完成的 bean 实例;

第二级缓存 :earlySingletonObjects,用于保存实例化完成的 bean 实例;

第三级缓存 :singletonFactories,用于保存 bean 创建工厂,以便后面有机会创建代理对象。

这是最核心

执行逻辑为:

1、先从“第一级缓存”找对象a,有就返回,没有就找“二级缓存”;

2、找“二级缓存”,有就返回,没有就找“三级缓存”;

3、找“三级缓存”,a找到了,就获取对象,放到“二级缓存”,并把a从“三级缓存”移除。

下面是Bean中的三级缓存的过程源码:

protected Object getSingleton(String beanName, boolean allowEarlyReference) {// Quick check for existing instance without full singleton lockObject singletonObject = this.singletonObjects.get(beanName);// 从一级缓存中获取// 如果一级缓存里没有,且 Bean 正在创建中// 就从二级缓存里获取if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {singletonObject = this.earlySingletonObjects.get(beanName);// 二级缓存没有,就从三级缓存获取一个工厂if (singletonObject == null && allowEarlyReference) {synchronized (this.singletonObjects) {// Consistent creation of early reference within full sinsingletonObject = this.singletonObjects.get(beanName);if (singletonObject == null) {singletonObject = this.earlySingletonObjects.get(beanName);if (singletonObject == null) {ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);if (singletonFactory != null) {// 能获取到工厂则创建 BeansingletonObject = singletonFactory.getObject();// 把实例存入二级缓存this.earlySingletonObjects.put(beanName, singletonObject);// 把工厂从三级缓存移除this.singletonFactories.remove(beanName);}}}}}}return singletonObject;
}

1、大概的思路:

当我们需要创建 A 的的时候,会首先通过 Java 反射创建出来一个原始的 A,这个原始 A 可以简单理解为刚刚 new 出来还没设置任何属性的 A,此时,我们把这个 A 先存入到一个缓存池中。接下来给 A 的属性设置值和解决A A 的依赖,这时我们发现 A 依赖 B,那么就去创建 B 对象,结果创建 B 的时候,发现 B 依赖 A,那么此时就先从缓存池中取出来 A(半成品A,啥属性也没有) 先用着,然后继续 B 创建的后续流程,直到 B 创建完成后,将之赋值给 A,此时 A 和 B 就都创建完成了。

如下图:

注意:

这里拿到的半成品A是个引用对象,也就是说它是个地址,所以即使里面的属性赋值都没有做也没关系,后面运行调用时拿到的就是完整的A

2、执行过程:

A半完成:

我们在创建A的时候,会通过反射创建出原始的A,接下来检查一缓存中有没有Bean,如果没有,则首先向三级缓存中添加一条记录,记录的 key 就是当前 Bean 的 beanName,value 则是一个 Lambda 表达式 ObjectFactory,通过执行这个 Lambda 可以给当前 A 生成代理对象。然后如果二级缓存中存在当前 A Bean,则移除掉。

B完成:

这样,A的前半部分就完成了,接下来给A的各个属性赋值,赋值时发现需要B,就去创建B,B和A一样,经历到给属性赋值的阶段,发现需要A,就去一级缓存找A,找不到就去二级缓存,再去三级缓存,找到A的ObjectFactory,然后执行这里的 getObject 方法,这个方法在执行的过程中,会去判断是否需要生成一个代理对象,如果需要就生成代理对象返回,如果不需要生成代理对象,则将原始对象返回即可。

最后,把拿到手的对象存入到二级缓存中以备下次使用,同时删除掉三级缓存中对应的数据。这样 A 所依赖的 B 就创建好了。

A完成:

再去完善B,属性赋值,A也就完成了。

注:

这里的解决思路需要在有AOP的情况下才可以,这里的Bean有ObjectFactory对象,否则就只能用二级缓存来解决。

二级缓存的解决大体思路是一样的,不过要把A的半成品存放在二级缓存中,这样可以更早的被调用。

二:@Lazy

延迟加载可以通过将 bean 的依赖关系运行时进行注入,而不是在初始化阶段

@Service
public class AserviceImpl implements Aservice {@Autowired@Lazyprivate Bservice bservice;@Asyncpublic  void test(){}}

Lazy 延迟加载打破循环依赖,他会通过其它途径生成bservice 的lazy 的代理对象,不会去走创建B 的代理 对象 然后注入A 这套流程。这样创建A 的单例对象并放入到单例池中,B 的bean 在实例化后,注入A bean 属性就可以从单例池中加载到A 的真正的bean ,而不会出现bean 对象不一致的问题。

相关文章:

Spring中Bean的循环依赖

目录 定义&#xff1a; 循环依赖的后果&#xff1a; 一&#xff1a;三级缓存 1、大概的思路&#xff1a; 注意&#xff1a; 2、执行过程&#xff1a; A半完成&#xff1a; B完成&#xff1a; A完成&#xff1a; 注&#xff1a; 二&#xff1a;Lazy 定义&#xff1a; …...

Java二十三种设计模式-代理模式模式(8/23)

代理模式&#xff1a;为对象访问提供灵活的控制 引言 代理模式&#xff08;Proxy Pattern&#xff09;是一种结构型设计模式&#xff0c;它为其他对象提供一个代替或占位符&#xff0c;以控制对它的访问。 基础知识&#xff0c;java设计模式总体来说设计模式分为三大类&#…...

Windows 11 家庭中文版 安装 VMWare 报 安装程序检测到主机启用了Hyper-V或Device

1、问题 我的操作系统信息如下&#xff1a; 我在安装 VMWare 的时候&#xff0c;报&#xff1a; 因为我之前安装了 docker 桌面版&#xff0c;所以才报这个提示。 安装程序检测到主机启用了 Hyper-v或 Device/credential Guard。要在启用了Hyper-或 Device/Credential Guard …...

机械学习—零基础学习日志(高数09——函数图形)

零基础为了学人工智能&#xff0c;真的开始复习高数 函数图像&#xff0c;开始新的学习&#xff01; 幂函数 利用函数的性质&#xff0c;以幂函数为例&#xff0c;因为单调性相同&#xff0c;利用图中的2和3公式&#xff0c;求最值问题&#xff0c;可以直接将式子进行简化。这…...

java迭代集合出现并发修改异常(ConcurrentModificationException)的原因以及解决方案

java迭代集合出现并发修改异常(ConcurrentModificationException)的原因以及解决方案 一. 什么时候会出现并发修改异常? 这里先看需求 : 定义一个集合,存储 唐僧,孙悟空,猪八戒,沙僧,遍历集合,如果遍历到猪八戒,往集合中添加一个白龙马 很显然要求我们先创建一个集合并进行…...

BGP选路之Local Preference

原理概述 当一台BGP路由器中存在多条去往同一目标网络的BGP路由时&#xff0c;BGP协议会对这些BGP路由的属性进行比较&#xff0c;以确定去往该目标网络的最优BGP路由。BGP首先比较的是路由信息的首选值&#xff08;PrefVal)&#xff0c;如果 PrefVal相同&#xff0c;就会比较本…...

WEB渗透信息收集篇--IP和端口信息

WEB渗透信息收集篇--域名信息-CSDN博客 WEB渗透信息收集篇--网站架构和指纹识别-CSDN博客 ​​​​​​​​​​​​​​WEB渗透信息收集篇--人员信息-CSDN博客​​​​​​​ WEB渗透信息收集篇--其他信息-CSDN博客 一、ASN ASN Tool - MxToolBox ASN通常指的是"自…...

国内微短剧系统平台抖音微信付费小程序app开发源代码交付

微短剧作为当下热门的内容&#xff0c;结合抖音平台的广泛用户基础&#xff0c;开发微短剧付费小程序APP具有显著的市场潜力&#xff0c;用户对于短剧内容的需求旺盛&#xff0c;特别是在言情、总裁、赘婿等热门题材方面&#xff0c;接下来给大家普及一下微短剧小程序系统。 顺…...

Java语言程序设计基础篇_编程练习题**15.19 (游戏:手眼协调)

**15.19 (游戏:手眼协调) 请编写一个程序&#xff0c;显示一个半径为10像素的实心圆&#xff0c;该圆放置在面板上的随机位置&#xff0c;并填充随机的顔色&#xff0c;如图15-29b所示。单击这个圆时&#xff0c;它会消失&#xff0c;然后在另一个随机的位置显示新的随机颜色的…...

学习记录day16—— 数据结构 双向链表 循环链表

双向链表 1、概念 1&#xff09;就是从任意一个节点既能存储其前驱节点&#xff0c;又能存储后继节点 2)结构体中增加一个指向前驱节点的指针 //定义数据类型 typedef int datatype;//定义节点类型 typedef struct Node {union {int len;datatype data;};struct Node *prio; …...

Air780EP模块 AT开发-MQTT接入OneNET移动物联网平台应用指南

应用概述 使用AT方式通过MQTT协议连接onenet studio。官网地址&#xff1a;https://open.iot.10086.cn/ 材料准备 Air780EP(V)开发板一套&#xff0c;包括天线SIM卡&#xff0c;USB线。 PC电脑&#xff0c;串口工具 在onenet上创建产品 打开OneNET官网&#xff0c;进入控制…...

HOST处理器预读PCI设备

在PCI&#xff08;Peripheral Component Interconnect&#xff09;总线规范中&#xff0c;MRL&#xff08;Memory Read Line&#xff09;和MRM&#xff08;Memory Read Multiple&#xff09;是两种读取存储器地址空间的总线事务类型。 MRL&#xff08;Memory Read Line&#xf…...

【Ansible】通过role角色部署lnmp架构

目录 一.roles概述 1.roles角色 2.roles的目录层次 2.1.roles 内各目录含义解释 二.实操 1.部署nginx 2.部署MySQL 3.部署php 4.编写测试文件 三.总结 一.roles概述 1.roles角色 可以把playbook剧本里的每个play看作为一个角色&#xff0c;将每个角色要用到的文件、…...

springboot给属性赋值的两种方式(yaml与properties)

一&#xff0c;介绍 在Spring Boot中&#xff0c;配置文件是用来设置应用程序的各种参数和操作模式的重要部分。Spring Boot支持两种主要类型的配置文件&#xff1a;properties文件和YAML 文件。这两种文件都可以用来定义相同的配置&#xff0c;但它们在格式和表达能力上有所不…...

20240725 每日AI必读资讯

&#x1f680;最强开源模型来了!Llama3.1以405B参数领先GPT-4o - Llama3.1以405B参数领先GPT-4o和Claude3.5Sonnet&#xff0c;在性能上实现超越。 - Meta大幅优化训练栈&#xff0c;扩展模型算力规模至16000个H100GPU&#xff0c;提高性能。 - Llama3.1具有上下文长度扩展、…...

17_高级进程间通信 UNIX域套接字1

非命名的UNIX域套接字 第1个参数domain&#xff0c;表示协议族&#xff0c;只能为AF_LOCAL或者AF_UNIX&#xff1b; 第2个参数type&#xff0c;表示类型&#xff0c;只能为0。 第3个参数protocol&#xff0c;表示协议&#xff0c;可以是SOCK_STREAM或者SOCK_DGRAM。用SOCK_STR…...

大型语言模型的生物医学知识图优化提示生成

大型语言模型的生物医学知识图优化提示生成 https://arxiv.org/abs/2311.17330 https://github.com/BaranziniLab/KG_RAG 大型语言模型的生物医学知识图优化提示生成 摘要 KG-RAG框架&#xff0c;较好的结合了生物医学知识图谱SPOKE和LLM的优势。SPOKE是一个开放知识图谱&…...

winform datagrid 全部勾选

如果我们想要进行全选或全部取消&#xff0c;在数据较多的情况下&#xff0c;这种方法显然特别繁琐。怎么办呢&#xff1f; 当然是加以一个全选按钮了&#xff0c;选中全选按钮则全选&#xff0c;否则取消。笔者本想在红色圆圈位置添加全选复选框的&#xff0c;那样看起来更加…...

从 NextJS SSRF 漏洞看 Host 头滥用所带来的危害

前言 本篇博文主要内容是通过代码审计以及场景复现一个 NextJS 的安全漏洞&#xff08;CVE-2024-34351&#xff09;来讲述滥用 Host 头的危害。 严正声明&#xff1a;本博文所讨论的技术仅用于研究学习&#xff0c;旨在增强读者的信息安全意识&#xff0c;提高信息安全防护技能…...

LC617-合并二叉树

文章目录 1 题目描述2 思路优化代码完整输入输出 参考 1 题目描述 https://leetcode.cn/problems/merge-two-binary-trees/description/ 给你两棵二叉树&#xff1a; root1 和 root2 。 将其中一棵覆盖到另一棵之上时&#xff0c;两棵树上的一些节点将会重叠&#xff08;而另…...

<6>-MySQL表的增删查改

目录 一&#xff0c;create&#xff08;创建表&#xff09; 二&#xff0c;retrieve&#xff08;查询表&#xff09; 1&#xff0c;select列 2&#xff0c;where条件 三&#xff0c;update&#xff08;更新表&#xff09; 四&#xff0c;delete&#xff08;删除表&#xf…...

1688商品列表API与其他数据源的对接思路

将1688商品列表API与其他数据源对接时&#xff0c;需结合业务场景设计数据流转链路&#xff0c;重点关注数据格式兼容性、接口调用频率控制及数据一致性维护。以下是具体对接思路及关键技术点&#xff1a; 一、核心对接场景与目标 商品数据同步 场景&#xff1a;将1688商品信息…...

MMaDA: Multimodal Large Diffusion Language Models

CODE &#xff1a; https://github.com/Gen-Verse/MMaDA Abstract 我们介绍了一种新型的多模态扩散基础模型MMaDA&#xff0c;它被设计用于在文本推理、多模态理解和文本到图像生成等不同领域实现卓越的性能。该方法的特点是三个关键创新:(i) MMaDA采用统一的扩散架构&#xf…...

k8s业务程序联调工具-KtConnect

概述 原理 工具作用是建立了一个从本地到集群的单向VPN&#xff0c;根据VPN原理&#xff0c;打通两个内网必然需要借助一个公共中继节点&#xff0c;ktconnect工具巧妙的利用k8s原生的portforward能力&#xff0c;简化了建立连接的过程&#xff0c;apiserver间接起到了中继节…...

3403. 从盒子中找出字典序最大的字符串 I

3403. 从盒子中找出字典序最大的字符串 I 题目链接&#xff1a;3403. 从盒子中找出字典序最大的字符串 I 代码如下&#xff1a; class Solution { public:string answerString(string word, int numFriends) {if (numFriends 1) {return word;}string res;for (int i 0;i &…...

Yolov8 目标检测蒸馏学习记录

yolov8系列模型蒸馏基本流程&#xff0c;代码下载&#xff1a;这里本人提交了一个demo:djdll/Yolov8_Distillation: Yolov8轻量化_蒸馏代码实现 在轻量化模型设计中&#xff0c;**知识蒸馏&#xff08;Knowledge Distillation&#xff09;**被广泛应用&#xff0c;作为提升模型…...

视觉slam十四讲实践部分记录——ch2、ch3

ch2 一、使用g++编译.cpp为可执行文件并运行(P30) g++ helloSLAM.cpp ./a.out运行 二、使用cmake编译 mkdir build cd build cmake .. makeCMakeCache.txt 文件仍然指向旧的目录。这表明在源代码目录中可能还存在旧的 CMakeCache.txt 文件,或者在构建过程中仍然引用了旧的路…...

【SSH疑难排查】轻松解决新版OpenSSH连接旧服务器的“no matching...“系列算法协商失败问题

【SSH疑难排查】轻松解决新版OpenSSH连接旧服务器的"no matching..."系列算法协商失败问题 摘要&#xff1a; 近期&#xff0c;在使用较新版本的OpenSSH客户端连接老旧SSH服务器时&#xff0c;会遇到 "no matching key exchange method found"​, "n…...

PHP 8.5 即将发布:管道操作符、强力调试

前不久&#xff0c;PHP宣布了即将在 2025 年 11 月 20 日 正式发布的 PHP 8.5&#xff01;作为 PHP 语言的又一次重要迭代&#xff0c;PHP 8.5 承诺带来一系列旨在提升代码可读性、健壮性以及开发者效率的改进。而更令人兴奋的是&#xff0c;借助强大的本地开发环境 ServBay&am…...

关于easyexcel动态下拉选问题处理

前些日子突然碰到一个问题&#xff0c;说是客户的导入文件模版想支持部分导入内容的下拉选&#xff0c;于是我就找了easyexcel官网寻找解决方案&#xff0c;并没有找到合适的方案&#xff0c;没办法只能自己动手并分享出来&#xff0c;针对Java生成Excel下拉菜单时因选项过多导…...