基于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计算程序开始到程序结束的运行时间和程序运行的CPU时间
Nov 28 Python
用Python编写一个国际象棋AI程序
Nov 28 Python
使用FastCGI部署Python的Django应用的教程
Jul 22 Python
python人民币小写转大写辅助工具
Jun 20 Python
解决python3 pika之连接断开的问题
Dec 18 Python
微信公众号token验证失败解决方案
Jul 22 Python
python3安装crypto出错及解决方法
Jul 30 Python
Django stark组件使用及原理详解
Aug 22 Python
Python单元测试与测试用例简析
Nov 09 Python
基于python检查SSL证书到期情况代码实例
Apr 04 Python
python制作图形界面的2048游戏, 基于tkinter
Apr 06 Python
python的列表生成式,生成器和generator对象你了解吗
Mar 16 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
php中使用DOM类读取XML文件的实现代码
2011/12/14 PHP
用Javascript实现锚点(Anchor)间平滑跳转
2009/09/08 Javascript
设置iframe的document.designMode后仅Firefox中其body.innerHTML为br
2012/02/27 Javascript
jQuery简单图表peity.js使用示例
2014/05/02 Javascript
window.open()实现post传递参数
2015/03/12 Javascript
jQuery实现类似标签风格的导航菜单效果代码
2015/08/25 Javascript
开启BootStrap学习之旅
2016/05/04 Javascript
Javascript将双字节字符转换成单字节字符并计算长度
2016/06/22 Javascript
jquery轮播的实现方式 附完整实例
2016/07/28 Javascript
JavaScript 监控微信浏览器且自带返回按钮时间
2016/11/27 Javascript
详解用原生JavaScript实现jQuery的某些简单功能
2016/12/19 Javascript
div中文字内容溢出常见的解决方法
2017/03/16 Javascript
react-native ListView下拉刷新上拉加载实现代码
2017/08/03 Javascript
小程序Request的另类用法详解
2019/08/09 Javascript
在vue中阻止浏览器后退的实例
2019/11/06 Javascript
vue实现移动端项目多行文本溢出省略
2020/07/29 Javascript
addEventListener()和removeEventListener()追加事件和删除追加事件
2020/12/04 Javascript
举例讲解如何在Python编程中进行迭代和遍历
2016/01/19 Python
详解字典树Trie结构及其Python代码实现
2016/06/03 Python
Python中pygal绘制雷达图代码分享
2017/12/07 Python
python pytest进阶之xunit fixture详解
2019/06/27 Python
解决Tensorflow sess.run导致的内存溢出问题
2020/02/05 Python
python使用布隆过滤器的实现示例
2020/08/20 Python
CSS3教程(8):CSS3透明度指南
2009/04/02 HTML / CSS
美国面料纺织品商城:Fabric.com
2017/06/28 全球购物
中东地区为妈妈们提供一切的头号购物目的地:Sprii
2018/05/06 全球购物
北京华建集团SQL面试题
2014/06/03 面试题
大学运动会通讯稿
2014/01/28 职场文书
教师开学感言
2014/02/14 职场文书
环保建议书
2014/03/12 职场文书
党建示范点实施方案
2014/03/12 职场文书
2014年教师教学工作总结
2014/11/08 职场文书
如何写好一份优秀的工作总结?
2019/06/21 职场文书
500字作文之难忘的同学
2019/12/20 职场文书
MySQL系列之八 MySQL服务器变量
2021/07/02 MySQL
OpenCV项目实践之停车场车位实时检测
2022/04/11 Python