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的循环依赖
目录 定义: 循环依赖的后果: 一:三级缓存 1、大概的思路: 注意: 2、执行过程: A半完成: B完成: A完成: 注: 二:Lazy 定义: …...
Java二十三种设计模式-代理模式模式(8/23)
代理模式:为对象访问提供灵活的控制 引言 代理模式(Proxy Pattern)是一种结构型设计模式,它为其他对象提供一个代替或占位符,以控制对它的访问。 基础知识,java设计模式总体来说设计模式分为三大类&#…...
Windows 11 家庭中文版 安装 VMWare 报 安装程序检测到主机启用了Hyper-V或Device
1、问题 我的操作系统信息如下: 我在安装 VMWare 的时候,报: 因为我之前安装了 docker 桌面版,所以才报这个提示。 安装程序检测到主机启用了 Hyper-v或 Device/credential Guard。要在启用了Hyper-或 Device/Credential Guard …...
机械学习—零基础学习日志(高数09——函数图形)
零基础为了学人工智能,真的开始复习高数 函数图像,开始新的学习! 幂函数 利用函数的性质,以幂函数为例,因为单调性相同,利用图中的2和3公式,求最值问题,可以直接将式子进行简化。这…...
java迭代集合出现并发修改异常(ConcurrentModificationException)的原因以及解决方案
java迭代集合出现并发修改异常(ConcurrentModificationException)的原因以及解决方案 一. 什么时候会出现并发修改异常? 这里先看需求 : 定义一个集合,存储 唐僧,孙悟空,猪八戒,沙僧,遍历集合,如果遍历到猪八戒,往集合中添加一个白龙马 很显然要求我们先创建一个集合并进行…...
BGP选路之Local Preference
原理概述 当一台BGP路由器中存在多条去往同一目标网络的BGP路由时,BGP协议会对这些BGP路由的属性进行比较,以确定去往该目标网络的最优BGP路由。BGP首先比较的是路由信息的首选值(PrefVal),如果 PrefVal相同,就会比较本…...
WEB渗透信息收集篇--IP和端口信息
WEB渗透信息收集篇--域名信息-CSDN博客 WEB渗透信息收集篇--网站架构和指纹识别-CSDN博客 WEB渗透信息收集篇--人员信息-CSDN博客 WEB渗透信息收集篇--其他信息-CSDN博客 一、ASN ASN Tool - MxToolBox ASN通常指的是"自…...
国内微短剧系统平台抖音微信付费小程序app开发源代码交付
微短剧作为当下热门的内容,结合抖音平台的广泛用户基础,开发微短剧付费小程序APP具有显著的市场潜力,用户对于短剧内容的需求旺盛,特别是在言情、总裁、赘婿等热门题材方面,接下来给大家普及一下微短剧小程序系统。 顺…...
Java语言程序设计基础篇_编程练习题**15.19 (游戏:手眼协调)
**15.19 (游戏:手眼协调) 请编写一个程序,显示一个半径为10像素的实心圆,该圆放置在面板上的随机位置,并填充随机的顔色,如图15-29b所示。单击这个圆时,它会消失,然后在另一个随机的位置显示新的随机颜色的…...
学习记录day16—— 数据结构 双向链表 循环链表
双向链表 1、概念 1)就是从任意一个节点既能存储其前驱节点,又能存储后继节点 2)结构体中增加一个指向前驱节点的指针 //定义数据类型 typedef int datatype;//定义节点类型 typedef struct Node {union {int len;datatype data;};struct Node *prio; …...
Air780EP模块 AT开发-MQTT接入OneNET移动物联网平台应用指南
应用概述 使用AT方式通过MQTT协议连接onenet studio。官网地址:https://open.iot.10086.cn/ 材料准备 Air780EP(V)开发板一套,包括天线SIM卡,USB线。 PC电脑,串口工具 在onenet上创建产品 打开OneNET官网,进入控制…...
HOST处理器预读PCI设备
在PCI(Peripheral Component Interconnect)总线规范中,MRL(Memory Read Line)和MRM(Memory Read Multiple)是两种读取存储器地址空间的总线事务类型。 MRL(Memory Read Line…...
【Ansible】通过role角色部署lnmp架构
目录 一.roles概述 1.roles角色 2.roles的目录层次 2.1.roles 内各目录含义解释 二.实操 1.部署nginx 2.部署MySQL 3.部署php 4.编写测试文件 三.总结 一.roles概述 1.roles角色 可以把playbook剧本里的每个play看作为一个角色,将每个角色要用到的文件、…...
springboot给属性赋值的两种方式(yaml与properties)
一,介绍 在Spring Boot中,配置文件是用来设置应用程序的各种参数和操作模式的重要部分。Spring Boot支持两种主要类型的配置文件:properties文件和YAML 文件。这两种文件都可以用来定义相同的配置,但它们在格式和表达能力上有所不…...
20240725 每日AI必读资讯
🚀最强开源模型来了!Llama3.1以405B参数领先GPT-4o - Llama3.1以405B参数领先GPT-4o和Claude3.5Sonnet,在性能上实现超越。 - Meta大幅优化训练栈,扩展模型算力规模至16000个H100GPU,提高性能。 - Llama3.1具有上下文长度扩展、…...
17_高级进程间通信 UNIX域套接字1
非命名的UNIX域套接字 第1个参数domain,表示协议族,只能为AF_LOCAL或者AF_UNIX; 第2个参数type,表示类型,只能为0。 第3个参数protocol,表示协议,可以是SOCK_STREAM或者SOCK_DGRAM。用SOCK_STR…...
大型语言模型的生物医学知识图优化提示生成
大型语言模型的生物医学知识图优化提示生成 https://arxiv.org/abs/2311.17330 https://github.com/BaranziniLab/KG_RAG 大型语言模型的生物医学知识图优化提示生成 摘要 KG-RAG框架,较好的结合了生物医学知识图谱SPOKE和LLM的优势。SPOKE是一个开放知识图谱&…...
winform datagrid 全部勾选
如果我们想要进行全选或全部取消,在数据较多的情况下,这种方法显然特别繁琐。怎么办呢? 当然是加以一个全选按钮了,选中全选按钮则全选,否则取消。笔者本想在红色圆圈位置添加全选复选框的,那样看起来更加…...
从 NextJS SSRF 漏洞看 Host 头滥用所带来的危害
前言 本篇博文主要内容是通过代码审计以及场景复现一个 NextJS 的安全漏洞(CVE-2024-34351)来讲述滥用 Host 头的危害。 严正声明:本博文所讨论的技术仅用于研究学习,旨在增强读者的信息安全意识,提高信息安全防护技能…...
LC617-合并二叉树
文章目录 1 题目描述2 思路优化代码完整输入输出 参考 1 题目描述 https://leetcode.cn/problems/merge-two-binary-trees/description/ 给你两棵二叉树: root1 和 root2 。 将其中一棵覆盖到另一棵之上时,两棵树上的一些节点将会重叠(而另…...
简易版抽奖活动的设计技术方案
1.前言 本技术方案旨在设计一套完整且可靠的抽奖活动逻辑,确保抽奖活动能够公平、公正、公开地进行,同时满足高并发访问、数据安全存储与高效处理等需求,为用户提供流畅的抽奖体验,助力业务顺利开展。本方案将涵盖抽奖活动的整体架构设计、核心流程逻辑、关键功能实现以及…...
【第二十一章 SDIO接口(SDIO)】
第二十一章 SDIO接口 目录 第二十一章 SDIO接口(SDIO) 1 SDIO 主要功能 2 SDIO 总线拓扑 3 SDIO 功能描述 3.1 SDIO 适配器 3.2 SDIOAHB 接口 4 卡功能描述 4.1 卡识别模式 4.2 卡复位 4.3 操作电压范围确认 4.4 卡识别过程 4.5 写数据块 4.6 读数据块 4.7 数据流…...
1688商品列表API与其他数据源的对接思路
将1688商品列表API与其他数据源对接时,需结合业务场景设计数据流转链路,重点关注数据格式兼容性、接口调用频率控制及数据一致性维护。以下是具体对接思路及关键技术点: 一、核心对接场景与目标 商品数据同步 场景:将1688商品信息…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院挂号小程序
一、开发准备 环境搭建: 安装DevEco Studio 3.0或更高版本配置HarmonyOS SDK申请开发者账号 项目创建: File > New > Create Project > Application (选择"Empty Ability") 二、核心功能实现 1. 医院科室展示 /…...
python爬虫:Newspaper3k 的详细使用(好用的新闻网站文章抓取和解析的Python库)
更多内容请见: 爬虫和逆向教程-专栏介绍和目录 文章目录 一、Newspaper3k 概述1.1 Newspaper3k 介绍1.2 主要功能1.3 典型应用场景1.4 安装二、基本用法2.2 提取单篇文章的内容2.2 处理多篇文档三、高级选项3.1 自定义配置3.2 分析文章情感四、实战案例4.1 构建新闻摘要聚合器…...
ETLCloud可能遇到的问题有哪些?常见坑位解析
数据集成平台ETLCloud,主要用于支持数据的抽取(Extract)、转换(Transform)和加载(Load)过程。提供了一个简洁直观的界面,以便用户可以在不同的数据源之间轻松地进行数据迁移和转换。…...
Spring AI 入门:Java 开发者的生成式 AI 实践之路
一、Spring AI 简介 在人工智能技术快速迭代的今天,Spring AI 作为 Spring 生态系统的新生力量,正在成为 Java 开发者拥抱生成式 AI 的最佳选择。该框架通过模块化设计实现了与主流 AI 服务(如 OpenAI、Anthropic)的无缝对接&…...
[Java恶补day16] 238.除自身以外数组的乘积
给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法,且在 O(n) 时间复杂度…...
第 86 场周赛:矩阵中的幻方、钥匙和房间、将数组拆分成斐波那契序列、猜猜这个单词
Q1、[中等] 矩阵中的幻方 1、题目描述 3 x 3 的幻方是一个填充有 从 1 到 9 的不同数字的 3 x 3 矩阵,其中每行,每列以及两条对角线上的各数之和都相等。 给定一个由整数组成的row x col 的 grid,其中有多少个 3 3 的 “幻方” 子矩阵&am…...
HarmonyOS运动开发:如何用mpchart绘制运动配速图表
##鸿蒙核心技术##运动开发##Sensor Service Kit(传感器服务)# 前言 在运动类应用中,运动数据的可视化是提升用户体验的重要环节。通过直观的图表展示运动过程中的关键数据,如配速、距离、卡路里消耗等,用户可以更清晰…...
