Spring框架中的循环依赖详解以及解决方案

ops/2024/11/13 9:02:10/

前言

在Spring框架中,依赖注入(DI)是实现控制反转(IoC)的核心机制。然而,在复杂的应用中,循环依赖(两个或多个bean相互依赖)是一个常见问题。如果不妥善处理,循环依赖会导致应用启动失败。本文将详细介绍Spring如何处理循环依赖,并提供解决方案。

什么是循环依赖?

循环依赖是指在Spring容器中,两个或多个bean之间存在直接或间接的依赖关系,形成一个闭环。例如,类A依赖类B,同时类B也依赖类A。

Spring如何解决循环依赖?

Spring框架提供了两种主要机制来解决循环依赖:

三级缓存机制:

一级缓存(singletonObjects):已经完全初始化好的对象。
二级缓存(earlySingletonObjects):提前暴露的对象,还没有完全初始化(即还没完成依赖注入)。
三级缓存(singletonFactories):存储bean工厂对象,用于解决循环依赖问题,可以通过它创建bean的早期引用。

解决循环依赖的流程
创建BeanA:

Spring开始创建BeanA,并进行属性填充。
在属性填充过程中,需要注入BeanB。

创建BeanB:

Spring开始创建BeanB,并进行属性填充。
在属性填充过程中,需要注入BeanA。

检测到循环依赖:

此时,BeanA还未完全初始化,Spring将BeanA的早期引用放入二级缓存。
同时,BeanA的工厂对象被放入三级缓存。

完成BeanB的创建:

Spring使用BeanA的早期引用完成BeanB的创建。
BeanB被放入一级缓存。

返回到BeanA的创建:

现在BeanB已经可用,Spring从二级缓存中获取BeanA的早期引用,并完成BeanA的创建。
BeanA最终也被放入一级缓存。

后续请求:

如果后续有其他bean需要注入BeanA或BeanB,Spring将直接从一级缓存中提供这些bean。

依赖注入的方法:

在创建bean时,Spring首先尝试从一级缓存中获取bean。如果未找到,则从二级缓存中获取bean的早期引用,并注入到其他依赖它的bean中。

Spring的循环依赖处理策略

构造器注入:

Spring不支持通过构造器注入解决循环依赖,因为构造器注入在创建bean实例时就需要所有的依赖。

字段注入和setter注入:

Spring支持通过字段注入和setter注入解决循环依赖。这两种方式允许bean在创建过程中被提前暴露,从而实现依赖注入。

解决方案

避免循环依赖:

重新设计应用的结构,避免组件之间的循环依赖。这是最根本的解决方案。

使用Setter注入:

将依赖注入的方式从构造器注入改为setter注入或字段注入,以允许Spring处理循环依赖。

使用@Lazy注解:

在某些情况下,可以使用@Lazy注解延迟依赖的加载,从而避免循环依赖。

@Autowired
@Lazy
private SomeBean someBean;

使用ApplicationContext获取Bean:

在某些复杂的场景下,可以通过ApplicationContext手动获取bean,但这种方式需要谨慎使用。

使用@DependsOn

在某些情况下,可以通过@DependsOn注解来明确指定bean的创建顺序,从而避免循环依赖:

@Component
@DependsOn("beanB")
public class BeanA {// ...
}

利用ApplicationContext的事件监听

Spring的ApplicationContext提供了多种事件,如ContextRefreshedEvent,可以在容器刷新完成后触发。这些事件可以用来执行一些在依赖注入后需要进行的操作,从而避免循环依赖。

总结

循环依赖是Spring应用开发中需要特别注意的问题。通过理解Spring的依赖注入机制和循环依赖处理策略,可以有效地避免和解决循环依赖问题。合理设计应用结构和谨慎使用依赖注入,是确保Spring应用顺利启动和运行的关键。


good day!!!


http://www.ppmy.cn/ops/107307.html

相关文章

无法用 FileZilla 传送文件的解决方案

以下内容源于学习过程中的总结,欢迎交流。 参考博客 (1)无法用FileZilla 传送文件的解决方案_filezilla could not transfer-CSDN博客 连接时的设置如下,可见我没有以root用户身份进行连接。 我打算把某个文件从本地PC机传送到虚…

吐血整理 ChatGPT 3.5/4.0 新手使用手册~ 【2024.08.30 更新】

以前我也是通过官网使用,但是经常被封号,就非常不方便,后来有朋友推荐国内工具,用了一阵之后,发现:稳定方便,用着也挺好的。 最新的 GPT-4o、4o mini,可搭配使用~ 1、 最新模型科普&…

【2024数学建模国赛赛题解析已出】原创免费分享

2024数模国赛赛题已正式发布 数模加油站初步分析评估了此次竞赛题目: A题:偏数学仿真建模,难度偏难,适合数学专业背景的同学 B题:评价决策类,自由度大,容易水,适合基础不太好的同…

CTFHub技能树-备份文件下载-bak文件

当开发人员在线上环境中对源代码进行了备份操作,并且将备份文件放在了 web 目录下,就会引起网站源码泄露。 使用dirsearch扫描出index.php.bak 有些时候网站管理员可能为了方便,会在修改某个文件的时候先复制一份,将其命名为xxx.b…

Notepad++的高级功能及插件使用说明(含安装包)

Notepad 的高级功能和插件使得这款文本编辑器更加强大和灵活。以下是一些常用的高级功能和插件的使用说明: 最新安装包免费下载地址 1. 高级功能 1.1 多文档和多视图 水平/垂直分屏:你可以通过 “查看” -> “分屏” 选项来将编辑区分成多个视图&…

Python文件自动分类

假如这样的步骤全部手动做下来耗时是6秒,在文件数量不多的情况下,比如10个文件,总共耗时一分钟其实是能够接受的。 但当文件数量特别多时,或者这个操作特别频繁每天都要做十几二十次时,手动操作就会变得耗时又繁琐…

学习计算机网络

a类0~127,b类128~191,c类192~223 网络地址:看子网掩码,分网络位和主机位,后面是主机位,主机位全部为0,网络地址。 直接广播地址:看子网掩码,分网络位和主机位&#xff…

Vue(十二) Vuex、四个map方法的使用、Vuex模块化+namespace命名空间

文章目录 一、Vuex前言:求和案例1. 搭建Vuex环境2. 基本使用3. 常见疑惑4. getters5. 四个map方法的使用(1) mapState(2) mapGetters(3) mapActions(4) mapMutations 6. 模块化命名空间namespace6.1 模块化6.2 模块化后读取数据 一、Vuex Vuex是一个Vue插件&#x…