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

Spring 循环依赖详解

Spring 循环依赖详解

1. 引言

在Spring框架中,依赖注入(Dependency Injection, DI)是其核心功能之一,它通过配置来管理对象的创建和它们之间的依赖关系。然而,在复杂的应用程序中,开发人员有时会遇到循环依赖的问题,即Bean A依赖于Bean B,而Bean B又依赖于Bean A。如果不加以处理,这种情况会导致应用程序无法启动。在本文中,我们将深入探讨Spring循环依赖的原理、处理机制、最佳实践以及可能遇到的问题。

2. 什么是循环依赖?

循环依赖是指两个或多个Bean相互依赖,形成一个闭环。例如,假设有两个Bean,Bean A和Bean B:

  • Bean A依赖于Bean B
  • Bean B依赖于Bean A

这种依赖关系就形成了一个循环,导致Spring容器在初始化Bean时无法确定哪个Bean应先创建。

3. Spring循环依赖的分类

根据依赖注入的方式不同,循环依赖可以分为以下几种类型:

3.1 构造器循环依赖

构造器循环依赖是指两个或多个Bean通过构造器参数相互依赖。例如:

public class BeanA {private final BeanB beanB;public BeanA(BeanB beanB) {this.beanB = beanB;}
}public class BeanB {private final BeanA beanA;public BeanB(BeanA beanA) {this.beanA = beanA;}
}

3.2 属性循环依赖

属性循环依赖是指两个或多个Bean通过属性相互依赖。例如:

public class BeanA {private BeanB beanB;public void setBeanB(BeanB beanB) {this.beanB = beanB;}
}public class BeanB {private BeanA beanA;public void setBeanA(BeanA beanA) {this.beanA = beanA;}
}

4. Spring如何解决循环依赖

Spring框架通过三级缓存(三级缓存机制)来解决大多数情况下的循环依赖问题。三级缓存机制包括以下三个层次:

  1. 单例池(singletonObjects):用于存放完全初始化好的单例Bean。
  2. 早期曝光对象池(earlySingletonObjects):用于存放部分初始化完成的单例Bean,通常是通过提前暴露Bean引用来解决循环依赖。
  3. 三级缓存(singletonFactories):用于存放Bean工厂,以便在需要时创建Bean实例。

4.1 三级缓存机制详解

4.1.1 单例池(singletonObjects)

单例池是一个Map,用于存放完全初始化好的单例Bean。当Spring容器创建一个Bean时,会首先检查单例池中是否已经存在该Bean,如果存在则直接返回,否则继续创建。

4.1.2 早期曝光对象池(earlySingletonObjects)

早期曝光对象池是一个Map,用于存放部分初始化完成的单例Bean。当Spring容器检测到循环依赖时,会将部分初始化完成的Bean提前放入该池中,以便其他Bean能够引用。

4.1.3 三级缓存(singletonFactories)

三级缓存是一个Map,用于存放Bean工厂。Bean工厂是一个用于创建Bean实例的对象,当需要创建Bean实例时,Spring容器会从三级缓存中获取相应的Bean工厂,并通过它来创建Bean实例。

4.2 三级缓存的工作流程

  1. Spring容器创建Bean A,首先检查单例池中是否存在Bean A。
  2. 如果单例池中不存在Bean A,则检查早期曝光对象池中是否存在Bean A。
  3. 如果早期曝光对象池中也不存在Bean A,则从三级缓存中获取Bean A的工厂,并通过该工厂创建Bean A的实例。
  4. 创建Bean A实例的过程中,发现Bean A依赖于Bean B,因此开始创建Bean B。
  5. 创建Bean B的过程中,发现Bean B依赖于Bean A,此时检测到循环依赖。
  6. 将Bean A的实例放入早期曝光对象池中,以便Bean B可以引用。
  7. 继续完成Bean B的创建,并将其放入单例池中。
  8. 返回Bean B的实例,继续完成Bean A的创建,并将其放入单例池中。

通过上述流程,Spring容器可以成功处理大多数情况下的循环依赖。

5. 实践中的循环依赖

5.1 避免构造器循环依赖

构造器循环依赖是无法通过Spring的三级缓存机制解决的,因为构造器循环依赖会导致Spring无法实例化任何一个Bean。解决这种问题的方法有:

  1. 重构代码,避免循环依赖。
  2. 使用Setter方法注入而不是构造器注入。

5.2 使用@Lazy注解

在某些情况下,可以使用@Lazy注解来延迟Bean的初始化,从而避免循环依赖。例如:

public class BeanA {@Autowired@Lazyprivate BeanB beanB;
}public class BeanB {@Autowiredprivate BeanA beanA;
}

5.3 使用代理对象

使用代理对象也是解决循环依赖的一种方法。Spring AOP(面向切面编程)通过动态代理机制创建Bean的代理对象,可以在一定程度上缓解循环依赖的问题。

6. Spring循环依赖的潜在问题

尽管Spring可以通过三级缓存机制解决大多数情况下的循环依赖,但在实际开发中,循环依赖仍可能导致一些潜在的问题:

  1. 代码难以维护:循环依赖会使代码逻辑复杂,增加代码的维护难度。
  2. 性能问题:频繁使用三级缓存可能会影响性能,特别是在Bean数量较多的情况下。
  3. 潜在的内存泄漏:不正确的依赖管理可能导致内存泄漏,从而影响应用程序的稳定性。

7. 总结

Spring循环依赖是一个复杂的问题,理解其工作原理和解决机制对于开发高质量的Spring应用程序至关重要。通过合理的设计和最佳实践,可以有效避免和解决循环依赖,确保应用程序的稳定性和可维护性。

在本篇文章中,我们深入探讨了Spring循环依赖的概念、分类、解决机制以及实际开发中的最佳实践。希望通过这些内容,能够帮助读者更好地理解和应对Spring循环依赖问题。


8. 扩展阅读

对于想要进一步深入了解Spring循环依赖的读者,可以参考以下资料:

  1. 《Spring实战》:本书详细介绍了Spring框架的核心概念和使用方法。
  2. Spring官方文档:Spring官方文档是了解Spring框架最新特性和最佳实践的重要资源。
  3. GitHub上的Spring源码:通过阅读Spring源码,可以深入了解Spring内部的工作机制和实现细节。

通过这些扩展阅读,读者可以进一步提高对Spring循环依赖的理解和应对能力。

相关文章:

Spring 循环依赖详解

Spring 循环依赖详解 1. 引言 在Spring框架中,依赖注入(Dependency Injection, DI)是其核心功能之一,它通过配置来管理对象的创建和它们之间的依赖关系。然而,在复杂的应用程序中,开发人员有时会遇到循环…...

项目经理真的不能太“拧巴”

前期的项目经理经常是“拧巴”的,就是心里纠结、思路混乱、行动迟缓。对于每天需要面对各种挑战、协调各方资源、确保项目顺利进行的项目经理来说,这种“拧巴”不仅会让自己陷入内耗中,还会让项目出大问题。 项目计划总是改来改去&#xff0…...

企业如何选择合适的CRM工具?除Salesforce之外的10大主流选择

对比salesforce,其他10款优秀CRM:纷享销客CRM、Zoho CRM、腾讯企点、销售易、企业微信 (WeCom)、Odoo CR、OroCRM、金蝶、用友CRM、EspoCRM 虽然Salesforce以其全面的功能和强大的市场占有率在海外收获了许多客户,但Salesforce在国内市场的接…...

每年1-1.2万人毕业,男女比例约3:1,测绘工程的就业率如何

测绘工程,一个让人闻风丧胆的理科专业,虎扑评分4.2: 干过测绘的,苦不苦只有大家心里知道,带大家来感受一下,兄弟们的精神状态都十分美妙: 测绘专业到底是什么情况? PS.测绘分为本科…...

JimuReport 积木报表 v1.7.6 版本发布,免费的低代码报表

项目介绍 一款免费的数据可视化报表工具,含报表和大屏设计,像搭建积木一样在线设计报表!功能涵盖,数据报表、打印设计、图表报表、大屏设计等! Web 版报表设计器,类似于excel操作风格,通过拖拽完…...

“灵活就业者“超两亿人 游戏开发者如何破局?

随着“灵活就业”者数量突破两亿,我相信“寒气”已经传递到每一位普通人!对于游戏行业的“灵活就业”者,应当如何破局? 首先应该恭喜大家,选择了一个相对“稳健”的行业,无论大环境如何,游戏/软…...

MySQL事务与存储引擎

一、事务的概念 是一种机制、一个操作序列,包含了一组数据库操作命令,并且把所有的命令作为一个整体一起向系统提交或撤销操作请求,即这一组数据库命令要么都执行,要么都不执行是一个不可分割的工作逻辑单元,在数据库…...

总是给数据库表字段设置默认值的好处

1、NOT NULL DEFAULT 的好处 在设计数据库表结构时,将字段设置为不能为空并设置默认值有以下几种好处: 1.1、数据完整性 通过设置字段不能为空,可以确保每条记录都包含必要的数据,从而保证了数据的完整性。例如,在用…...

11.2 Go 常用包介绍

💝💝💝欢迎莅临我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」…...

Sqlite3数据库基本使用

一、基本概念 数据:能够输入计算机并能被计算机程序识别和处理的信息集合 数据库:长期存储在计算机内、有组织的、可共享的大量数据的集合 DBMS:位于用户与操作系统之间的一层数据管理软件,用于操纵和管理数据库 二、安装 在线…...

实现贪吃蛇小游戏【简单版】

1. 贪吃蛇游戏设计与分析 1.1 地图 我们最终的贪吃蛇大纲要是这个样子,那我们的地图如何布置呢? 这里不得不讲⼀下控制台窗口的⼀些知识,如果想在控制台的窗口中指定位置输出信息,我们得知道该位置的坐标,所以首先介…...

uniapp实现内嵌其他网页的功能

一、用到的知识点 页面跳转页面间跳转&#xff0c;参数传递web-view使用 二、使用navigator 页面跳转。 navigator 组件类似HTML中的<a>组件&#xff0c;但只能跳转本地页面。目标页面必须在pages.json中注册。所以这么写是不行的&#xff1a; <navigator url&quo…...

【Ruby简单脚本01】查看wifi密码

脚本 # 使用io库 def get_cmd_result(cmd) IO.popen(cmd,:external_encoding>GBK).read.encode("utf-8") end def list_wifi wifi_pwds Hash.new # 获取所有wifi文件 o1 get_cmd_result("netsh wlan show profiles") # 获取所有匹配结果 …...

VSG/VSA 矢量信号模拟/分析软件

_Ceyear思仪 _ VSG/VSA 矢量信号模拟/分析软件 苏州新利通仪器仪表 在现代无线通信中&#xff0c;IQ调制属于标准配置&#xff0c;经常应用于通信系统的信号调制和解调环节。IQ调制的应用简化了通信设备的硬件结构&#xff0c;同时提高了频谱资源的利用效率&#xff0c;提…...

C++使用GDAL库完成tiff图像的合并

全色图 完整代码&#xff1a; #include "gdal_priv.h" #include "cpl_string.h" #include <vector> #include <algorithm> #include <iostream> #include <filesystem>using namespace std; namespace fs std::filesystem; vec…...

深入理解AQS:Java并发编程中的核心组件

目录 AQS简介AQS的设计思路AQS的核心组成部分 状态&#xff08;State&#xff09;同步队列&#xff08;Sync Queue&#xff09;条件队列&#xff08;Condition Queue&#xff09; AQS的内部实现 节点&#xff08;Node&#xff09;锁的获取与释放 独占锁共享锁 条件变量 AQS的应…...

集合进阶:List集合

一.List集合的特有方法 1.Collection的方法List都继承了 2.List集合因为有索引,所以多了很多索引操作的方法。 3.add // 1.创建一个集合List<String> list new ArrayList<>(); // 2.添加元素list.add("aaa");list.add("bbb"…...

el-table表头修改文字或者背景颜色,通过header-row-style设置样式

方式一 <el-table :header-cell-style"{text-align: center}" />方式二 <template><el-table :header-cell-style"tableHeaderColor" /> </template> <script> export default {methods: {tableHeaderColor ({row, column…...

web前端-CSS

CSS CSS概述: CSS是Cascading Style Sheets&#xff08;级联样式表&#xff09;,是一种样式表语言,用于控制网页布局,外观(比如背景图片,图片高度,文本颜色,文本字体,高级定位等等) 可将页面的内容与样式分离开,样式放于单独的.css文件或者HTML某处 CSS是网页样式,HTML是网页…...

u8g2 使用IIC驱动uc1617 lcd 字符显示只显示上半部分,不显示下半部

使用u8g2 使用硬件iic驱动某些page为4个字节 带灰度的lcd显示屏幕的时候有时候只显示上半部,下半部不显示,例如uc1617等。 原因: 以uc1617为例,链接https://github.com/olikraus/u8g2/blob/master/csrc/u8x8_d_uc1617.c 在u8x8_d_uc1617_common方法中的case U8X8_MSG_DI…...

Docker 离线安装指南

参考文章 1、确认操作系统类型及内核版本 Docker依赖于Linux内核的一些特性&#xff0c;不同版本的Docker对内核版本有不同要求。例如&#xff0c;Docker 17.06及之后的版本通常需要Linux内核3.10及以上版本&#xff0c;Docker17.09及更高版本对应Linux内核4.9.x及更高版本。…...

黑马Mybatis

Mybatis 表现层&#xff1a;页面展示 业务层&#xff1a;逻辑处理 持久层&#xff1a;持久数据化保存 在这里插入图片描述 Mybatis快速入门 ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/6501c2109c4442118ceb6014725e48e4.png //logback.xml <?xml ver…...

线程与协程

1. 线程与协程 1.1. “函数调用级别”的切换、上下文切换 1. 函数调用级别的切换 “函数调用级别的切换”是指&#xff1a;像函数调用/返回一样轻量地完成任务切换。 举例说明&#xff1a; 当你在程序中写一个函数调用&#xff1a; funcA() 然后 funcA 执行完后返回&…...

AtCoder 第409​场初级竞赛 A~E题解

A Conflict 【题目链接】 原题链接&#xff1a;A - Conflict 【考点】 枚举 【题目大意】 找到是否有两人都想要的物品。 【解析】 遍历两端字符串&#xff0c;只有在同时为 o 时输出 Yes 并结束程序&#xff0c;否则输出 No。 【难度】 GESP三级 【代码参考】 #i…...

为什么需要建设工程项目管理?工程项目管理有哪些亮点功能?

在建筑行业&#xff0c;项目管理的重要性不言而喻。随着工程规模的扩大、技术复杂度的提升&#xff0c;传统的管理模式已经难以满足现代工程的需求。过去&#xff0c;许多企业依赖手工记录、口头沟通和分散的信息管理&#xff0c;导致效率低下、成本失控、风险频发。例如&#…...

在四层代理中还原真实客户端ngx_stream_realip_module

一、模块原理与价值 PROXY Protocol 回溯 第三方负载均衡&#xff08;如 HAProxy、AWS NLB、阿里 SLB&#xff09;发起上游连接时&#xff0c;将真实客户端 IP/Port 写入 PROXY Protocol v1/v2 头。Stream 层接收到头部后&#xff0c;ngx_stream_realip_module 从中提取原始信息…...

高危文件识别的常用算法:原理、应用与企业场景

高危文件识别的常用算法&#xff1a;原理、应用与企业场景 高危文件识别旨在检测可能导致安全威胁的文件&#xff0c;如包含恶意代码、敏感数据或欺诈内容的文档&#xff0c;在企业协同办公环境中&#xff08;如Teams、Google Workspace&#xff09;尤为重要。结合大模型技术&…...

unix/linux,sudo,其发展历程详细时间线、由来、历史背景

sudo 的诞生和演化,本身就是一部 Unix/Linux 系统管理哲学变迁的微缩史。来,让我们拨开时间的迷雾,一同探寻 sudo 那波澜壮阔(也颇为实用主义)的发展历程。 历史背景:su的时代与困境 ( 20 世纪 70 年代 - 80 年代初) 在 sudo 出现之前,Unix 系统管理员和需要特权操作的…...

图表类系列各种样式PPT模版分享

图标图表系列PPT模版&#xff0c;柱状图PPT模版&#xff0c;线状图PPT模版&#xff0c;折线图PPT模版&#xff0c;饼状图PPT模版&#xff0c;雷达图PPT模版&#xff0c;树状图PPT模版 图表类系列各种样式PPT模版分享&#xff1a;图表系列PPT模板https://pan.quark.cn/s/20d40aa…...

学习STC51单片机32(芯片为STC89C52RCRC)OLED显示屏2

每日一言 今天的每一份坚持&#xff0c;都是在为未来积攒底气。 案例&#xff1a;OLED显示一个A 这边观察到一个点&#xff0c;怎么雪花了就是都是乱七八糟的占满了屏幕。。 解释 &#xff1a; 如果代码里信号切换太快&#xff08;比如 SDA 刚变&#xff0c;SCL 立刻变&#…...