Spring之循环依赖

什么是循环依赖?

依赖的相互引用,如下列的这种形式

@Component
public class A {@Autowiredprivate B b;}@Component
public class B {@Autowiredprivate A a;
}

Spring是如何解决循环依赖的

Spring是通过三级缓存来解决循环依赖

  • singletonObjects : 单例bean,已经实例化,完成属性填充
  • earlySingletonObjects : 半成品bean,已经实例化,未完成属性填充
  • singletonFactories : 一个函数式接口

为什么要使用三级缓存?

因为Spring的动态代理流程是在属性填充流程之后的,如果只使用二级缓存,半成品bean中不确定是放一个普通对象还是代理对象,所以需要使用三级缓存来保存一个不确定的值

PS : 如果只使用二级缓存,那必须所有对象都进行动态代理,那样会生成很多class文件,浪费内存

图解一般情况下的循环依赖

Spring真的能完全解决循环依赖? 

改造一下上文代码

@Component
public class A {private B b;public A(B b) {this.b = b;}
}@Component
public class B {@Autowiredprivate A a;
}

很遗憾,Spring没能解决这种构造器与setter注入形式的循环依赖,我们来分析具体原因

前置知识点

  1. 如果一个类有且仅有一个有参构造方法,Spring会以此构造方法实例化对象
  2. 实例化对象之前,会先处理参数的依赖。比如上述代码中bean A的创建过程中,会先getBean(b),再实例化对象A

源码分析

AbstractBeanFactory#doGetBean

我们先看一下这个getSingleton方法本身,而不是createBean方法

由上述源码我们可以得知:执行createBean方法之前会将beanName加入到singletonsCurrentlyInCreation列表中,表明这个bean正在创建

后续b进行属性填充的时候会先从singletonFactories中获取a,因为a还没有进行实例化,没有放入singletonFactories中,所以又会执行getSingleton方法,因为singletonsCurrentlyInCreation这个列表已经存在a这个beanName,所以if条件成立,抛出异常

图解流程

构造方法与setter注入的循环依赖,如果构造方法对应的bean先处理,则会抛出异常,后处理则不会抛出异常。

注意 : bean A 还没有实例化,singletonFactories​​中还没有提前暴露的对象

如果将上述代码改下列形式,则不会抛出异常

@Component
public class A {@Autowiredprivate B b;
}@Component
public class B {private A a;public B(A a) {this.a = a;}
}

PS :针对A先实例化,B后实例化 

综上所述

  • setter和setter的循环依赖:正确注入
  • 构造方法和setter的循环依赖:抛出异常
  • setter和构造方法的循环依赖:正确注入
  • 构造方法和构造方法的循环依赖:抛出异常

普通情况下,我们标记bean正在创建,后续会将实例对象提前暴露放入singletonFactories中,但是构造方法和setter方式的循环依赖在标记bean正在创建后,并没有立刻将实例对象放入singletonFactories中,而是处理构造方法中参数所对应的依赖,如果这个参数对应的依赖,循环依赖了当前bean,则会抛出异常。即标记正在创建的bean,在对象提前暴露放入singletonFactories之前,不能注入一个依赖当前bean的bean

Spring如何解决循环依赖?

SpringBoot2.6及以上版本默认不支持循环依赖,那如何解决循环依赖呢?

主要原因就是SpringBoot将allowCircularReferences默认值设置为false

从配置角度

1.在application.properties文件中将该值设置为true

spring.main.allow-circular-references=true

2.使用bfpp将该值设置为true

@Component
public class FirstBeanFactoryPostProcessor implements BeanDefinitionRegistryPostProcessor {@Overridepublic void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) registry;defaultListableBeanFactory.setAllowCircularReferences(true);}@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {}
}
从代码角度

1.使用@Lazy注解

@Component
public class A {@Autowired@Lazyprivate B b;}@Component
public class B {@Autowiredprivate A a;}

2.使用@Lookup注解

@Component
public class A {private B b;@Lookuppublic B getB() {return this.b;}
}@Component
public class B {@Autowiredprivate A a;}

3.实现ApplicationContextAware接口

@Component
public class A implements ApplicationContextAware {private B b;private ApplicationContext applicationContext;@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {this.applicationContext = applicationContext;this.b = applicationContext.getBean(B.class);}}@Component
public class B {@Autowiredprivate A a;}

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

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

相关文章

牛客2024年愚人节比赛(A-K)

比赛链接 毕竟是娱乐场,放平心态打吧。。。 只有A一个考了数学期望,其他的基本都是acmer特有的脑筋急转弯,看个乐呵即可。 A 我是欧皇,赚到盆满钵满! 思路: 我们有 p 1 p_1 p1​ 的概率直接拿到一件实…

CMake常用示例

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

【代码随想录】day32

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 一、122买卖股票的最佳时机II二、55跳跃游戏三、45跳跃游戏II 一、122买卖股票的最佳时机II 方法1:计算斜率大于0的线段的diffY class Solution { public:int max…

Redis改造原始代码

基础篇Redis 5.2.2.改造原始代码 代码说明: 1.在我们完成了使用工厂设计模式来完成代码的编写之后,我们在获得连接时,就可以通过工厂来获得。 ,而不用直接去new对象,降低耦合,并且使用的还是连接池对象。 2.当我们…

FreeROST作业day2

1.总结串口的发送和接收功能使用到的函数 串口发送数据函数: HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, const uint8_t *pData, uint16_t Size, uint32_t Timeout); UART_Handle…

C语言经典例题(16) --- 按照格式输入并交换输出、计算机表达式的值、能活多少秒、喝酸奶、竞选社长

1.按照格式输入并交换输出 题目描述: 输入两个整数,范围-231~231-1,交换两个数并输出。 输入描述: 输入只有一行,按照格式输入两个整数,范围,中间用“,”分隔。 输出描述: 把两个整数按格式输出,中间用“…

【LeetCode】热题100:排序链表

题目: 给你链表的头结点 head ,请将其按 升序 排列并返回 排序后的链表 。 示例 1: 输入:head [4,2,1,3] 输出:[1,2,3,4] 示例 2: 输入:head [-1,5,3,4,0] 输出:[-1,0,3,4,5] …

linux进程fork函数的讲解。

通过指令,查看接口的详细信息 man forkOn success, the PID of the child process is returned in the parent, and 0 is returned in the child. On failure, -1 is returned in the parent, no child process is created, and errno is set appropriately. 这里的返回值的意…

【Java笔记】Lambda表达式

文章目录 回顾下InterfaceWhy InterfaceInterface例子需求实现需求 Lambda表达式Why LambdaLambda表达式Lambda表达式实现send()无参数的抽象方法有参数的抽象方法多参数、有返回值的抽象方法赋值给变量Email or SMS? Lambda表达式的限制Reference Lambda表达式是Ja…

FPGA设计_加法器

文章目录 前言补充:各种门电路符号一、半加器二、全加器三、串行进位加法器3.1、verilog代码设计 四、超前进位加法器4.1、verilog代码设计 五、进位链CARRY4 前言 在之前一篇介绍7系列FPGA底层资源的时候,我们提到过每一个slice当中有一个CARRY4&#…

NodeJS 基础入门学习手册

什么是 Node.js 简单的说 Node.js 就是运行在服务端的 JavaScript。 Node.js 是一个基于 Chrome JavaScript 运行时建立的一个平台。 Node.js 是一个事件驱动 I/O 服务端 JavaScript 环境,基于 Google 的 V8 引擎,V8 引擎执行 Javascript 的速度非常快…

玫瑰图和雷达图(自备)

目录 玫瑰图 数据格式 绘图基础 绘图升级(文本调整) 玫瑰图 下载数据data/2020/2020-11-24 mirrors_rfordatascience/tidytuesday - 码云 - 开源中国 (gitee.com) R语言绘图—南丁格尔玫瑰图 - 知乎 (zhihu.com) 数据格式 rm(list ls()) libr…

2024年新算法-冠豪猪优化算法(CPO),CPO-RF-Adaboost,CPO优化随机森林RF-Adaboost回归预测-附代码

冠豪猪优化算法(CPO)是一种基于自然界中猪群觅食行为启发的优化算法。该算法模拟了猪群在寻找食物时的集群行为,通过一系列的迭代过程来优化目标函数,以寻找最优解。在这个算法中,猪被分为几个群体,每个群体…

CA根证书——https安全保障的基石

HTTPS通信中,服务器端使用数字证书来证明自己的身份。客户端需要验证服务器发送的证书的真实性。这就需要一个可信的第三方机构,即CA,来颁发和管理证书。CA根证书是证书颁发机构层次结构的顶级证书,客户端信任的所有证书都可以追溯…

[lesson01]学习C++的意义

学习C的意义 C语言特点 C语言是在实践的过程中逐步完善起来的 没有深思熟路的设计过程残留量过多低级语言的特征 C语言的目标是高效 最终程序执行效率的高效 软件方法论的发展 面相过程程序设计:数据结构 算法 主要解决科学计算问题,用户需求简单而…

python实现泊松回归

1 什么是基于计数的数据? 基于计数的数据包含以特定速率发生的事件。发生率可能会随着时间的推移或从一次观察到下一次观察而发生变化。以下是基于计数的数据的一些示例: 每小时穿过十字路口的车辆数量每月去看医生的人数每月发现的类地行星数量 计数数…

行车记录打不开?别慌,数据恢复有高招!

行车记录打不开,这恐怕是许多车主都曾经遭遇过的烦恼。在驾驶途中,行车记录仪本应是记录美好瞬间、保障行车安全的重要工具,但一旦它出现打不开的情况,所有的期待与信赖便瞬间化为乌有。面对这种情况,我们该如何应对&a…

web学习笔记(五十一)

目录 1. post请求和get请求的区别 2. CORS 跨域资源共享 2.1 什么是同源 2.2 什么是同源策略 2.3 如何实现跨域资源共享 2.4 使用 cors 中间件解决跨域问题 2.5 JSONP 接口 2.6 实现 JSONP 接口的步骤 1. post请求和get请求的区别 传参方式不同:get请求参数…

文本文件操作

大家好&#xff1a; 衷心希望各位点赞。 您的问题请留在评论区&#xff0c;我会及时回答。 文件操作 程序运行时&#xff0c;产生的数据都是临时数据&#xff0c;程序一旦运行结束都会被释放。通过文件可以将数据持久化。 C中对文件进行操作需要包含头文件<fstream> 文件…

2024年抖音小店的保证金是多少?真的可以做0元保证金的店铺吗?

大家好&#xff0c;我是电商糖果 2024年想要入驻抖音小店的商家依旧很多&#xff0c;关于小店的保证金问题也有不少人前来咨询。 大家问的最多的是可以开通0元保证金的店铺吗&#xff1f;以及2024年抖音小店保证金是多少&#xff1f; 这里糖果给大家一个个解答。 可以开通0…