基于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实现的生成自我描述脚本分享(很有意思的程序)
Jul 18 Python
python实现日常记账本小程序
Mar 10 Python
解决python 未发现数据源名称并且未指定默认驱动程序的问题
Dec 07 Python
Python3进制之间的转换代码实例
Aug 24 Python
Django项目后台不挂断运行的方法
Aug 31 Python
python如何通过pyqt5实现进度条
Jan 20 Python
Python安装whl文件过程图解
Feb 18 Python
Pycharm中配置远程Docker运行环境的教程图解
Jun 11 Python
自学python用什么系统好
Jun 23 Python
flask项目集成swagger的方法
Dec 09 Python
python缺失值的解决方法总结
Jun 09 Python
使用python生成大量数据写入es数据库并查询操作(2)
Sep 23 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中empty is_null和isset的测试
2013/06/29 PHP
PHP独立Session数据库存储操作类分享
2014/06/11 PHP
PHP中的插件机制原理和实例
2014/07/08 PHP
php获取服务器操作系统相关信息的方法
2016/10/08 PHP
Yii2.0实现的批量更新及批量插入功能示例
2019/01/29 PHP
JavaScript游戏之是男人就下100层代码打包
2010/11/08 Javascript
分享一个asp.net pager分页控件
2012/01/04 Javascript
javascript解析json数据的3种方式
2014/05/08 Javascript
JS中获取函数调用链所有参数的方法
2015/05/07 Javascript
JQuery通过AJAX从后台获取信息显示在表格上并支持行选中
2015/09/15 Javascript
在javascript中随机数 math random如何生成指定范围数值的随机数
2015/10/21 Javascript
深入理解AngularJs-scope的脏检查(一)
2017/06/19 Javascript
layer插件select选中默认值的方法
2018/08/14 Javascript
vue keep-alive 动态删除组件缓存的例子
2019/11/04 Javascript
Vue 数组和对象更新,但是页面没有刷新的解决方式
2019/11/09 Javascript
Django框架下在URLconf中指定视图缓存的方法
2015/07/23 Python
python机器人行走步数问题的解决
2018/01/29 Python
Python3连接SQLServer、Oracle、MySql的方法
2018/06/28 Python
详解用python自制微信机器人,定时发送天气预报
2019/03/25 Python
Python assert语句的简单使用示例
2019/07/28 Python
Python2比较当前图片跟图库哪个图片相似的方法示例
2019/09/28 Python
Python2和Python3中@abstractmethod使用方法
2020/02/04 Python
python使用hdfs3模块对hdfs进行操作详解
2020/06/06 Python
分享一个python的aes加密代码
2020/12/22 Python
Python Pygame实现俄罗斯方块
2021/02/19 Python
菲律宾酒店预订网站:Hotels.com菲律宾
2017/07/12 全球购物
员工趣味活动方案
2014/08/27 职场文书
2014乡镇班子个人对照检查材料思想汇报
2014/09/26 职场文书
2014年党员评议表自我评价
2014/09/27 职场文书
2016党校学习心得体会范文
2016/01/07 职场文书
学习型家庭事迹材料(2016精选版)
2016/02/29 职场文书
公司财务制度:成本管理控制制度模板
2019/11/19 职场文书
python实现股票历史数据可视化分析案例
2021/06/10 Python
SpringBoot集成Redis的思路详解
2021/10/16 Redis
大型强子对撞机再次重启探索“第五种自然力”
2022/04/29 数码科技
python数字图像处理:图像的绘制
2022/06/28 Python