系列二十、Spring循环依赖问题

news2024/11/25 7:06:58

一、概述

        循环依赖是指多个bean之间相互依赖,形成了一个闭环。比如A依赖于B、B依赖于C、C依赖于A,形成了一个圈,如:

二、循环依赖案例

2.1、构造方法注入产生循环依赖案例

2.1.1、ServiceA

/**
 * @Author : 一叶浮萍归大海
 * @Date: 2023/11/27 9:14
 * @Description:
 */
@Service
public class ServiceA {

    private ServiceB serviceB;

    public ServiceA(ServiceB serviceB) {
        this.serviceB = serviceB;
    }
}

2.1.2、ServiceB

/**
 * @Author : 一叶浮萍归大海
 * @Date: 2023/11/27 9:14
 * @Description:
 */
@Service
public class ServiceB {

    private ServiceA serviceA;

    public ServiceB(ServiceA serviceA) {
        this.serviceA = serviceA;
    }
}

2.1.3、MySpringConfig

/**
 * @Author : 一叶浮萍归大海
 * @Date: 2023/11/23 15:29
 * @Description:
 */
@Configuration
@ComponentScan(basePackages = {"org.star"})
public class MySpringConfig {

}

2.1.4、AopFullAnnotationMainApp

/**
 * @Author : 一叶浮萍归大海
 * @Date: 2023/11/23 15:14
 * @Description:
 */
@Slf4j
public class AopFullAnnotationMainApp {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MySpringConfig.class);
        ServiceA serviceA = context.getBean("serviceA", ServiceA.class);
        log.info("serviceA:{}", serviceA);
    }
}

2.1.5、结论

使用构造方法注入属性会产生循环依赖问题。

2.2、set方法注入不会产生循环依赖案例

2.2.1、ServiceAA

/**
 * @Author : 一叶浮萍归大海
 * @Date: 2023/11/27 9:27
 * @Description:
 */
@Service
public class ServiceAA {

    @Resource
    private ServiceBB serviceBB;

}

2.2.2、ServiceBB

/**
 * @Author : 一叶浮萍归大海
 * @Date: 2023/11/27 9:27
 * @Description:
 */
@Service
public class ServiceBB {

    @Resource
    private ServiceAA serviceAA;

}

2.2.3、MySpringConfig(同上)

2.2.4、AopFullAnnotationMainApp

/**
 * @Author : 一叶浮萍归大海
 * @Date: 2023/11/23 15:14
 * @Description:
 */
@Slf4j
public class AopFullAnnotationMainApp {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MySpringConfig.class);
        ServiceAA serviceAA = context.getBean(ServiceAA.class);
        log.info("serviceAA:{}", serviceAA);
    }
}

2.2.5、结论

使用set方法注入属性不会产生循环依赖问题。

2.2.6、注意事项

        默认情况下使用set方法注入属性可以解决循环依赖问题,但是有一个前提,bean是单例的,当把bean设置为多例后,再使用set注入时依然会产生循环依赖问题。

三、Spring解决循环依赖的方法

3.1、三级缓存

        所谓Spring的三级缓存是指Spring内部解决循环依赖问题的三个Map,即 DefaultSingletonBeanRegistry 中定义的三个Map。部分源码如下:

3.2、A、B对象在三级缓存中的迁移过程

第一步:A创建过程中需要B,于是A将自己放到三级缓存里面,去实例化B;

第二步:B实例化的时候发现需要A,于是B先查找一级缓存,没有,再查二级缓存,还是没有,再查三级缓存,于是找到A,然后把三级缓存里面的这个A放到二级缓存里面,并删除三级缓存里面的A;

第三步:B顺利完成初始化,并将自己放到一级缓存里面(注意:此时B里面的A依然是创建中的状态),然后接着回来创建A,此时B已经创建结束,直接从一级缓存里面拿到B,然后完成创建,并将A自己放到一级缓存里面。

3.3、断点顺序

# 参考B站尚硅谷周阳老师录制的视频,链接地址如下
 
https://www.bilibili.com/video/BV1Hy4y1B78T?p=39&spm_id_from=pageDriver&vd_source=72ebc194dbfa51ec950a52c25c337f7c

3.4、解决循环依赖的步骤

        第一步:Spring创建bean主要分为两个步骤,即:创建原始bean对象 & 填充对象属性和初始化;

        第二步:每次创建bean之前,Spring都会从缓存中查询该bean是否存在,如果缓存中存在,则直接拿来用,如果没有Spring则去创建原始对象并放入三级缓存中,因为是单例,只能有一个;

        第三步:当创建完A的原始对象后(此时A已经在三级缓存中了),接下来为A填充属性,这时候发现A依赖B,接着又去创建B,同样的流程,创建完B的原始对象后,填充属性时发现B又依赖于A,B实例化的时候需要A,于是B先从一级缓存里面查找A,没有找到,接着再查二级缓存,还是没有,再查三级缓存,于是找到了A,然后Spring把三级缓存里面的A放到二级缓存里面,并删除三级缓存里面的A,B顺利完成初始化,并将自己放入到一级缓存里面(注意:此时B里面的属性A依然是创建中的状态),然后接着回来继续为A填充属性,此时B已经创建结束,Spring直接从一级缓存中取出B,为A赋值,完成A的初始化,然后把A自己放入到一级缓存里面,并删除二级缓存里面的A,此时A,B各自完成实例化。

3.5、解决循环依赖的思想

        Spring解决循环依赖靠的是bean的 中间态 这个概念,而这个中间态指的是 已经实例化但是还没有初始化的状态,即:半成品;实例化的过程又是通过构造器完成的,如果A还没有创建出来怎么提前曝光?这也就解释了使用构造方法注入属性为什么无法解决循环依赖的问题。

Spring解决单例bean循环依赖的三个Map(三级缓存):

        # 一级缓存:单例池,存放已经经历了完整生命周期的bean

        private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

        # 二级缓存:存放早期暴露出来的bean对象,此时bean的生命周期尚未结束(属性还未填充完毕)

        private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

        # 三级缓存:存放可以生成bean的工厂

        private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

3.6、详细过程

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

力扣:183. 从不订购的客户(Python3)

题目&#xff1a; Customers 表&#xff1a; ---------------------- | Column Name | Type | ---------------------- | id | int | | name | varchar | ---------------------- 在 SQL 中&#xff0c;id 是该表的主键。 该表的每一行都表示客户的 ID …

QMap key()和value(const Key key, const T defaultValue = T()) const第二个参数作用

value()函数介绍 返回与键key关联的值。 如果map不包含键为key的项&#xff0c;则该函数返回defaultValue。 如果没有指定defaultValue&#xff0c;该函数返回一个默认构造的值。 如果映射中有多个key项&#xff0c;则返回最近插入的项的值。 示例 定义自定义类型&#xff…

MATLAB中fft与fftshift的区别

两者的区别在于&#xff1a; fft函数将时域信号转换为频域信号&#xff0c;即将信号从时间域转换为频率域。fftshift函数用于对fft计算结果进行移位操作&#xff0c;将频域信号的零频率分量移到频谱的中心&#xff0c;方便观察和处理。fftshift函数将fft计 算结果沿着中心点进…

2023年最后一次PMP项目管理认证考试成功举行!

2023年11月25日&#xff0c;PMI项目管理资格认证考试顺利举行&#xff01; 才聚集团作为目前国内规模最大的项目管理专业考试服务机构&#xff0c;此次考试负责深圳、广州、东莞、珠海、南宁、保定6个城市的现场考务工作&#xff0c;各考点的布置&#xff0c;入场&#xff0c;…

门店预约系统会员小程序作用如何

除了线上卖货外&#xff0c;很多行业或商家的产品/服务需要进店&#xff0c;因此一款完善的门店预约系统是众商家需要的。 通过【雨科】平台搭建门店预约小程序&#xff0c;通过预约功能&#xff0c;客户可以查看服务并选择规格、时间等填写信息预约&#xff0c;客户与商家同时…

CMake语法解读 | Qt6需要用到

CMake 入门CMakeLists.txtmain.cpp编译示例cmake常用参数入门 Hello CMake CMake 是一个用于配置跨平台源代码项目应该如何配置的工具建立在给定的平台上。 ├── CMakeLists.txt # 希望运行的 CMake命令 ├── main.cpp # 带有main 的源文件 ├── include # 头文件目录 …

【C++】多态(上) 多态 | 虚函数 | 重写 | final、override | 接口继承与实现继承 | 抽象类

一、多态 概念 多态&#xff0c;就是多种状态&#xff0c;即不同的对象去完成同一个行为时会产生出不同的状态。比如&#xff1a;买票时&#xff0c;成人要原价买&#xff0c;学生和老人就可以享受优惠价便宜一点儿。同样是买票这个行为&#xff0c;不同的对象来做就有不同的…

招募引流模式是实体门店吸引顾客的一种有效策略

在如今激烈的市场竞争和庞大的客户需求中&#xff0c;应该采取什么样的方式来应对&#xff0c;才能找到自己的一席之地。招募引流模式是实体门店吸引顾客的一种有效策略&#xff0c;通常招募体验官或合作伙伴&#xff0c;让他们协助门店进行推广活动&#xff0c;达到增加客流量…

Go——三、运算符以及流程控制

Go 一、Go语言运算符1、算数运算符2、关系运算符3、逻辑运算符4、位运算符5、赋值运算符6、其他运算符7、运算符优先级 二、Go的流程控制1、if else2、for 循环结构3、for range&#xff08;键值循环&#xff09;4、switch case5、break&#xff1a;跳出循环6、go&#xff1a;跳…

AIGC系列之:DDPM原理解读(简单易懂版)

目录 DDPM基本原理 DDPM中的Unet模块 Unet模块介绍 Unet流程示意图 DownBlock和UpBlock MiddleBlock 文生图模型的一般公式 总结 本文部分内容参考文章&#xff1a;https://juejin.cn/post/7251391372394053691&#xff0c;https://zhuanlan.zhihu.com/p/563661713&…

Selenium 连接到现有的 Firefox 示例

当前环境&#xff1a; python 3.7 selenium 3.14.1 urllib3 1.26.8 Frefox 115.1.0esr(32位) geckodriver.exe 0.33.0 1 下载 Firefox 浏览器&#xff0c;根据自己的需要选择。 下载 Firefox 浏览器&#xff0c;这里有简体中文及其他 90 多种语言版本…

为什么对中小企业来说,数字化转型很难?

通过整合尖端数字技术和创造性流程&#xff0c;实现公司运营和客户参与的现代化&#xff0c;被称为“数字化转型”。在当今瞬息万变的商业环境中&#xff0c;数字化转型已成为中小型企业寻求生存和可持续增长的关键要求。拥抱数字化转型对于企业的长期成功和可持续发展至关重要…

世微AP5125 DC-DC降压恒流 LED车灯电源驱动IC SOT23-6

产品描述 AP5125 是一款外围电路简单的 Buck 型平均电流检测模式的 LED 恒流驱动器&#xff0c;适用于 8-100V 电压范围的非隔离式大功率恒流 LED 驱动领域。芯片采用固定频率 140kHz 的 PWM 工作模式&#xff0c; 利用平均电流检测模式&#xff0c;因此具有优异的负载调整 率…

Day49:647. 回文子串、516.最长回文子序列

文章目录 647. 回文子串思路代码实现 516.最长回文子序列思路代码实现 647. 回文子串 题目链接 思路 确定dp数组&#xff08;dp table&#xff09;以及下标的含义 布尔类型的dp[i][j]&#xff1a;表示区间范围[i,j] &#xff08;注意是左闭右闭&#xff09;的子串是否是回文…

个微协议/ipad协议/协议/8.0.37协议

微信开发API接口WX/ipad/8.0.37协议&#xff0c;api可实现众多功能&#xff1b; 扫码登录、可对PYQ进行发布、查看、互动点赞、评论&#xff1b; 好友列表、微信消息收发、发文本消息、图片消息、名片消息、动图表情、发文件、删好友&#xff1b; 添加好友、微信转账接收、微…

【Spark入门】基础入门

【大家好&#xff0c;我是爱干饭的猿&#xff0c;本文重点介绍Spark的定义、发展、扩展阅读&#xff1a;Spark VS Hadoop、四大特点、框架模块、运行模式、架构角色。 后续会继续分享其他重要知识点总结&#xff0c;如果喜欢这篇文章&#xff0c;点个赞&#x1f44d;&#xff…

关于fine-tune “微调”

大模型的 Fine-tune 我们对技术的理解&#xff0c;要比技术本身更加重要。 正如我在《大模型时代的应用创新范式》一文中所说&#xff0c;大模型会成为AI时代的一项基础设施。 作为像水、电一样的基础设施&#xff0c;预训练大模型这样的艰巨任务&#xff0c;只会有少数技术…

美SEC与贝莱德,对比特币现货ETF申购方式产生分歧!

比特币现货ETF的通过时间是市场投资者密切关注的议题。虽然SEC最近推迟了Hashdex、富兰克林邓普顿&#xff08;Franklin Templeton&#xff09;和GlobalX申请的决议时间&#xff0c;但彭博ETF分析师James Seyffart对明年一月通过的机率持乐观态度&#xff0c;认为其通过的机会能…

Jmeter参数化之数据库读取数据

以读取mysql数据库为例 1.下载一个mysql驱动包&#xff0c;最好去mysql官网下载 下载网址&#xff1a;https://dev.mysql.com/downloads/connector/j/ elect Operating Systems&#xff1a;选择Platform independent 然后选择zip包&#xff0c;点击Download。 下载如下&…

【24届校招】c++选手还有机会吗?如何选择更好的出路?

一、今年为什么c选手就业形势如此艰难&#xff1f; 去年c岗位的火热&#xff0c;不少c选手拿到高薪offer&#xff0c;今年转c的人群变多&#xff0c;内卷加剧&#xff0c;高学历大佬多如牛毛&#xff0c;很多比较好的c岗位多人投递&#xff0c;僧多肉少。 从行情来说&#xf…