基于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 相关文章推荐
linux下python使用sendmail发送邮件
May 22 Python
django框架自定义模板标签(template tag)操作示例
Jun 24 Python
python操作excel让工作自动化
Aug 09 Python
python基础教程之while循环
Aug 14 Python
Python爬虫运用正则表达式的方法和优缺点
Aug 25 Python
复化梯形求积分实例——用Python进行数值计算
Nov 20 Python
详解Python实现进度条的4种方式
Jan 15 Python
基于python实现把json数据转换成Excel表格
May 07 Python
Python中使用aiohttp模拟服务器出现错误问题及解决方法
Oct 31 Python
python dir函数快速掌握用法技巧
Dec 09 Python
Python的scikit-image模块实例讲解
Dec 30 Python
Python OpenCV实现传统图片格式与base64转换
Jun 13 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 远程关机操作的代码
2008/12/05 PHP
php使用PDO方法详解
2014/12/27 PHP
PHP中使用Imagick读取pdf并生成png缩略图实例
2015/01/21 PHP
Thinkphp 中 distinct 的用法解析
2016/12/14 PHP
PHP 类与构造函数解析
2017/02/06 PHP
javascript 去字符串空格终极版(支持utf8)
2009/11/14 Javascript
jquery+ajax每秒向后台发送请求数据然后返回页面的代码
2011/01/17 Javascript
jQuery大于号(>)选择器的作用解释
2015/01/13 Javascript
javascript实现状态栏中文字动态显示的方法
2015/10/20 Javascript
JavaScript严格模式详解
2015/11/18 Javascript
javascript每日必学之条件分支
2016/02/17 Javascript
限制复选框最多选择项的实现代码
2016/05/30 Javascript
js手动播放图片实现图片轮播效果
2016/09/17 Javascript
详解nodejs中exports和module.exports的区别
2017/02/17 NodeJs
jQuery设置图片等比例缩小的方法
2017/04/29 jQuery
three.js中文文档学习之通过模块导入
2017/11/20 Javascript
关于react-router/react-router-dom v4 history不能访问问题的解决
2018/01/08 Javascript
详解vue2.0+axios+mock+axios-mock+adapter实现登陆
2018/07/19 Javascript
JointJS流程图的绘制方法
2018/12/03 Javascript
深入浅出了解Node.js Streams
2019/05/27 Javascript
Vue中遍历数组的新方法实例详解
2019/07/21 Javascript
python3读取MySQL-Front的MYSQL密码
2017/05/03 Python
开启Django博客的RSS功能的实现方法
2020/02/17 Python
Python编程快速上手——强口令检测算法案例分析
2020/02/29 Python
django model的update时auto_now不被更新的原因及解决方式
2020/04/01 Python
flask开启多线程的具体方法
2020/08/02 Python
python定义具名元组实例操作
2021/02/28 Python
使用CSS实现弹性视频html5案例实践
2012/12/26 HTML / CSS
Cotton On美国网站:澳洲时装连锁品牌
2016/10/25 全球购物
全球性的在线购物网站:Zapals
2017/03/22 全球购物
Java中有几种类型的流?JDK为每种类型的流提供了一些抽象类以供继承,请说出他们分别是哪些类
2012/02/06 面试题
王力宏牛津大学演讲稿
2014/05/22 职场文书
承诺书模板
2014/08/30 职场文书
老人节主持词
2015/07/04 职场文书
篮球比赛通讯稿
2015/07/18 职场文书
详解Redis的三种常用的缓存读写策略步骤
2022/05/06 Redis