基于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 ElementTree 基本读操作示例
Apr 09 Python
Python contextlib模块使用示例
Feb 18 Python
使用pdb模块调试Python程序实例
Jun 02 Python
python Django框架实现自定义表单提交
Mar 25 Python
win10系统下Anaconda3安装配置方法图文教程
Sep 19 Python
python列表list保留顺序去重的实例
Dec 14 Python
Python两台电脑实现TCP通信的方法示例
May 06 Python
Python实现Restful API的例子
Aug 31 Python
Python Scrapy框架:通用爬虫之CrawlSpider用法简单示例
Apr 11 Python
Python脚本实现监听服务器的思路代码详解
May 28 Python
Python matplotlib 绘制双Y轴曲线图的示例代码
Jun 12 Python
Python Celery异步任务队列使用方法解析
Aug 10 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中的函数嵌套层数限制分析
2011/06/13 PHP
thinkphp常见路径用法分析
2014/12/02 PHP
php使用mysqli向数据库添加数据的方法
2015/03/20 PHP
php异步:在php中使用fsockopen curl实现类似异步处理的功能方法
2016/12/10 PHP
jquery 圆形旋转图片滚动切换效果
2011/01/19 Javascript
基于jquery的$.ajax async使用
2011/10/19 Javascript
JS声明变量背后的编译原理剖析
2012/12/28 Javascript
AngularJs基于角色的前端访问控制的实现
2016/11/07 Javascript
AngularJS全局警告框实现方法示例
2017/05/18 Javascript
Node.js连接mongodb实例代码
2017/06/06 Javascript
基于vue中解决v-for使用报红并出现警告的问题
2018/03/03 Javascript
解决vue单页路由跳转后scrollTop的问题
2018/09/03 Javascript
JavaScript类的继承操作实例总结
2018/12/20 Javascript
bootstrap tooltips在 angularJS中的使用方法
2019/04/10 Javascript
微信小程序实现元素渐入渐出动画效果封装方法
2019/05/18 Javascript
react结合bootstrap实现评论功能
2020/05/30 Javascript
ansible作为python模块库使用的方法实例
2017/01/17 Python
详解Python 序列化Serialize 和 反序列化Deserialize
2017/08/20 Python
pandas数据处理基础之筛选指定行或者指定列的数据
2018/05/03 Python
Python多线程同步---文件读写控制方法
2019/02/12 Python
Python 函数返回值的示例代码
2019/03/11 Python
python 控制Asterisk AMI接口外呼电话的例子
2019/08/08 Python
Python在终端通过pip安装好包以后在Pycharm中依然无法使用的问题(三种解决方案)
2020/03/10 Python
基于python连接oracle导并出数据文件
2020/04/28 Python
HTML5页面无缝闪开的问题及解决方案
2020/06/11 HTML / CSS
意大利灯具购物网站:Lampade.it
2018/10/18 全球购物
PUMA澳大利亚官方网站:德国运动品牌
2018/10/19 全球购物
药物学专业学生的自我评价
2013/10/27 职场文书
车间班长岗位职责
2013/11/30 职场文书
服装设计专业自荐书范文
2013/12/30 职场文书
网络事业创业计划书范文
2014/01/09 职场文书
幼儿园区域活动总结
2014/05/08 职场文书
机电一体化专业毕业生自荐信
2014/06/19 职场文书
我们的节日元宵活动方案
2014/08/23 职场文书
安全生产月标语
2014/10/07 职场文书
Python快速实现一键抠图功能的全过程
2021/06/29 Python