基于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 时间处理datetime实例
Sep 06 Python
linux系统使用python监控apache服务器进程脚本分享
Jan 15 Python
Python中的CURL PycURL使用例子
Jun 01 Python
python使用wxpython开发简单记事本的方法
May 20 Python
python中的break、continue、exit()、pass全面解析
Aug 05 Python
Python装饰器用法示例小结
Feb 11 Python
python+POP3实现批量下载邮件附件
Jun 19 Python
python 分离文件名和路径以及分离文件名和后缀的方法
Oct 21 Python
python使用itchat模块给心爱的人每天发天气预报
Nov 25 Python
Python selenium实现断言3种方法解析
Sep 08 Python
Python读写csv文件流程及异常解决
Oct 20 Python
python 详解turtle画爱心代码
Feb 15 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
解决laravel-admin 自己新建页面里 js 需要刷新一次的问题
2019/10/03 PHP
PHP底层运行机制与工作原理详解
2020/07/31 PHP
TNC vs RR BO3 第一场 2.14
2021/03/10 DOTA
Javascript日期对象的dateAdd与dateDiff方法
2008/11/18 Javascript
JQuery加载图片自适应固定大小的DIV
2013/09/12 Javascript
深入解析桶排序算法及Node.js上JavaScript的代码实现
2016/07/06 Javascript
教大家轻松制作Bootstrap漂亮表格(table)
2016/12/13 Javascript
如何提高数据访问速度
2016/12/26 Javascript
JS字符串false转boolean的方法(推荐)
2017/03/08 Javascript
JQuery EasyUI的一些常用组件
2017/07/12 jQuery
js原生代码实现轮播图的实例讲解
2017/07/28 Javascript
vue项目中使用tinymce编辑器的步骤详解
2018/09/11 Javascript
vue2.0 如何在hash模式下实现微信分享
2019/01/22 Javascript
Node.js系列之连接DB的方法(3)
2019/08/30 Javascript
Layui点击图片弹框预览的实现方法
2019/09/16 Javascript
微信小程序地图绘制线段并且测量(实例代码)
2020/01/02 Javascript
十分钟教你上手ES2020新特性
2020/02/12 Javascript
Javascript模块化机制实现原理详解
2020/04/02 Javascript
[01:10:48]完美世界DOTA2联赛PWL S2 GXR vs PXG 第一场 11.18
2020/11/18 DOTA
python访问纯真IP数据库的代码
2011/05/19 Python
Python中使用OpenCV库来进行简单的气象学遥感影像计算
2016/02/19 Python
Python 字符串大小写转换的简单实例
2017/01/21 Python
Python常用时间操作总结【取得当前时间、时间函数、应用等】
2017/05/11 Python
python 表达式和语句及for、while循环练习实例
2017/07/07 Python
Python内置模块logging用法实例分析
2018/02/12 Python
使用django-crontab实现定时任务的示例
2018/02/26 Python
python绘制圆柱体的方法
2018/07/02 Python
python issubclass 和 isinstance函数
2019/07/25 Python
python 字符串常用方法汇总详解
2019/09/16 Python
Python3利用scapy局域网实现自动多线程arp扫描功能
2021/01/21 Python
anaconda安装pytorch1.7.1和torchvision0.8.2的方法(亲测可用)
2021/02/01 Python
CSS3结构性伪类选择器九种写法
2012/04/18 HTML / CSS
印尼披萨外送专家:Domino’s Pizza印尼
2017/12/28 全球购物
大学生创业项目方案
2014/03/08 职场文书
图书馆标语
2014/06/19 职场文书
自主招生自荐信格式
2015/03/04 职场文书