基于SpringBoot构造器注入循环依赖及解决方式


Posted in Python onApril 26, 2020

1. 循环依赖是什么?

Bean A 依赖 B,Bean B 依赖 A这种情况下出现循环依赖。

Bean A → Bean B → Bean A

更复杂的间接依赖造成的循环依赖如下。

Bean A → Bean B → Bean C → Bean D → Bean E → Bean A

2. 循环依赖会产生什么结果?

当Spring正在加载所有Bean时,Spring尝试以能正常创建Bean的顺序去创建Bean。

例如,有如下依赖:

Bean A → Bean B → Bean C

Spring先创建beanC,接着创建bean B(将C注入B中),最后创建bean A(将B注入A中)。

但当存在循环依赖时,Spring将无法决定先创建哪个bean。这种情况下,Spring将产生异常BeanCurrentlyInCreationException。

当使用构造器注入时经常会发生循环依赖问题。如果使用其它类型的注入方式能够避免这种问题。

3. 构造器注入循环依赖实例

首先定义两个相互通过构造器注入依赖的bean。

@Component
public class CircularDependencyA {
 
 private CircularDependencyB circB;
 
 @Autowired
 public CircularDependencyA(CircularDependencyB circB) {
  this.circB = circB;
 }
}
@Component
public class CircularDependencyB {
 
 private CircularDependencyA circA;
 
 @Autowired
 public CircularDependencyB(CircularDependencyA circA) {
  this.circA = circA;
 }
}
@Configuration
@ComponentScan(basePackages = { "com.baeldung.circulardependency" })
public class TestConfig {
}
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = { TestConfig.class })
public class CircularDependencyTest {
 
 @Test
 public void givenCircularDependency_whenConstructorInjection_thenItFails() {
  // Empty test; we just want the context to load
 }
}

运行方法givenCircularDependency_whenConstructorInjection_thenItFails将会产生异常:

BeanCurrentlyInCreationException: Error creating bean with name ‘circularDependencyA': Requested bean is currently in creation: Is there an unresolvable circular reference?

4.解决方法

处理这种问题目前有如下几种常见方式。

4.1 重新设计

重新设计结构,消除循环依赖。

4.2 使用注解 @Lazy

一种最简单的消除循环依赖的方式是通过延迟加载。在注入依赖时,先注入代理对象,当首次使用时再创建对象完成注入。

@Component
public class CircularDependencyA {
 
 private CircularDependencyB circB;
 
 @Autowired
 public CircularDependencyA(@Lazy CircularDependencyB circB) {
  this.circB = circB;
 }
}

使用@Lazy后,运行代码,可以看到异常消除。

4.3 使用Setter/Field注入

Spring文档建议的一种方式是使用setter注入。当依赖最终被使用时才进行注入。对前文的样例代码少做修改,来观察测试效果。

@Component
public class CircularDependencyA {
 
 private CircularDependencyB circB;
 
 @Autowired
 public void setCircB(CircularDependencyB circB) {
  this.circB = circB;
 }
 
 public CircularDependencyB getCircB() {
  return circB;
 }
}
@Component
public class CircularDependencyB {
 
 private CircularDependencyA circA;
 
 private String message = "Hi!";
 
 @Autowired
 public void setCircA(CircularDependencyA circA) {
  this.circA = circA;
 }
 
 public String getMessage() {
  return message;
 }
}
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = { TestConfig.class })
public class CircularDependencyTest {
 
 @Autowired
 ApplicationContext context;
 
 @Bean
 public CircularDependencyA getCircularDependencyA() {
  return new CircularDependencyA();
 }
 
 @Bean
 public CircularDependencyB getCircularDependencyB() {
  return new CircularDependencyB();
 }
 
 @Test
 public void givenCircularDependency_whenSetterInjection_thenItWorks() {
  CircularDependencyA circA = context.getBean(CircularDependencyA.class);

  Assert.assertEquals("Hi!", circA.getCircB().getMessage());
 }
}

4.4 使用@PostConstruct

@Component
public class CircularDependencyA {
 
 @Autowired
 private CircularDependencyB circB;
 
 @PostConstruct
 public void init() {
  circB.setCircA(this);
 }
 
 public CircularDependencyB getCircB() {
  return circB;
 }
}
@Component
public class CircularDependencyB {
 
 private CircularDependencyA circA;
  
 private String message = "Hi!";
 
 public void setCircA(CircularDependencyA circA) {
  this.circA = circA;
 }
  
 public String getMessage() {
  return message;
 }

4.5 实现ApplicationContextAware与InitializingBean

@Component
public class CircularDependencyA implements ApplicationContextAware, InitializingBean {
 
 private CircularDependencyB circB;
 
 private ApplicationContext context;
 
 public CircularDependencyB getCircB() {
  return circB;
 }
 
 @Override
 public void afterPropertiesSet() throws Exception {
  circB = context.getBean(CircularDependencyB.class);
 }
 
 @Override
 public void setApplicationContext(final ApplicationContext ctx) throws BeansException {
  context = ctx;
 }
}
@Component
public class CircularDependencyB {
 
 private CircularDependencyA circA;
 
 private String message = "Hi!";
 
 @Autowired
 public void setCircA(CircularDependencyA circA) {
  this.circA = circA;
 }
 
 public String getMessage() {
  return message;
 }
}

5.总结

处理循环依赖有多种方式。首先考虑是否能够通过重新设计依赖来避免循环依赖。如果确实需要循环依赖,那么可以通过前文提到的方式来处理。优先建议使用setter注入来解决。

以上这篇基于SpringBoot构造器注入循环依赖及解决方式就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持三水点靠木。

Python 相关文章推荐
Python导出数据到Excel可读取的CSV文件的方法
May 12 Python
Python 处理数据的实例详解
Aug 10 Python
Python中的pack和unpack的使用
Mar 12 Python
python pandas库中DataFrame对行和列的操作实例讲解
Jun 09 Python
Python 实例方法、类方法、静态方法的区别与作用
Aug 14 Python
python定位xpath 节点位置的方法
Aug 27 Python
python将字典列表导出为Excel文件的方法
Sep 02 Python
Pytorch中膨胀卷积的用法详解
Jan 07 Python
python 解决flask 图片在线浏览或者直接下载的问题
Jan 09 Python
检测tensorflow是否使用gpu进行计算的方式
Feb 03 Python
基于Tensorflow使用CPU而不用GPU问题的解决
Feb 07 Python
Python查找算法的实现 (线性、二分,分块、插值查找算法)
Apr 24 Python
Python判断字符串是否为空和null方法实例
Apr 26 #Python
如何将PySpark导入Python的放实现(2种)
Apr 26 #Python
基于python实现对文件进行切分行
Apr 26 #Python
python matplotlib模块基本图形绘制方法小结【直线,曲线,直方图,饼图等】
Apr 26 #Python
简单了解Java Netty Reactor三种线程模型
Apr 26 #Python
Python Selenium截图功能实现代码
Apr 26 #Python
使用Pycharm(Python工具)新建项目及创建Python文件的教程
Apr 26 #Python
You might like
destoon供应信息title调用出公司名称的方法
2014/08/22 PHP
PHP获取文件相对路径的方法
2015/02/26 PHP
javascript 字符串连接的性能问题(多浏览器)
2008/11/18 Javascript
javascript中不提供sleep功能如何实现这个功能
2014/05/27 Javascript
使用OpenLayers3 添加地图鼠标右键菜单
2015/12/29 Javascript
jQuery实现的点赞随机数字显示动画效果(附在线演示与demo源码下载)
2015/12/31 Javascript
jQuery中inArray方法注意事项分析
2016/01/25 Javascript
深入理解js中的加载事件
2017/02/08 Javascript
使用vue2实现带地区编号和名称的省市县三级联动效果
2018/11/05 Javascript
微信小程序canvas.drawImage完全显示图片问题的解决
2018/11/30 Javascript
在移动端使用vue-router和keep-alive的方法示例
2018/12/02 Javascript
vue之a-table中实现清空选中的数据
2019/11/07 Javascript
js实现简单的随机点名器
2020/09/17 Javascript
python启动办公软件进程(word、excel、ppt、以及wps的et、wps、wpp)
2009/04/09 Python
python ip正则式
2009/05/07 Python
详解Python中列表和元祖的使用方法
2015/04/25 Python
python抓取文件夹的所有文件
2018/02/27 Python
Python基于多线程实现抓取数据存入数据库的方法
2018/06/22 Python
Python过滤txt文件内重复内容的方法
2018/10/21 Python
python生成以及打开json、csv和txt文件的实例
2018/11/16 Python
使用Python3+PyQT5+Pyserial 实现简单的串口工具方法
2019/02/13 Python
使用python实现数组、链表、队列、栈的方法
2019/12/20 Python
Python Pickle 实现在同一个文件中序列化多个对象
2019/12/30 Python
python爬虫实现获取下一页代码
2020/03/13 Python
用python实现前向分词最大匹配算法的示例代码
2020/08/06 Python
python实现excel公式格式化的示例代码
2020/12/23 Python
Python Spyder 调出缩进对齐线的操作
2021/02/26 Python
css3弹性盒子flex实现三栏布局的实现
2020/11/12 HTML / CSS
使用索引(Index)有哪些需要考虑的因素
2016/10/19 面试题
小学教师的个人自我鉴定
2013/10/26 职场文书
十八届三中全会个人学习材料
2014/02/13 职场文书
出纳试用期自我鉴定
2014/04/07 职场文书
委托证明模板
2014/09/16 职场文书
户外活动总结
2015/02/04 职场文书
MySQL Innodb关键特性之插入缓冲(insert buffer)
2021/04/08 MySQL
python基于tkinter制作m3u8视频下载工具
2021/04/24 Python