基于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之入门(二)基本数据类型
May 25 Python
Python常用的日期时间处理方法示例
Feb 08 Python
Django验证码的生成与使用示例
May 20 Python
详解Python 多线程 Timer定时器/延迟执行、Event事件
Jun 27 Python
Python OpenCV之图片缩放的实现(cv2.resize)
Jun 28 Python
python实现用户名密码校验
Mar 18 Python
解决django接口无法通过ip进行访问的问题
Mar 27 Python
Pytorch之Tensor和Numpy之间的转换的实现方法
Sep 03 Python
python3.7中安装paddleocr及paddlepaddle包的多种方法
Nov 27 Python
python 实现的车牌识别项目
Jan 25 Python
Python中os模块的简单使用及重命名操作
Apr 17 Python
Python绘制散点图之可视化神器pyecharts
Jul 07 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
初学者入门:细述PHP4的核心Zend
2006/09/05 PHP
PHP网站提速三大“软”招
2006/10/09 PHP
php curl 伪造IP来源的实例代码
2012/11/01 PHP
PHP中mb_convert_encoding与iconv函数的深入解析
2013/06/21 PHP
Yii2框架可逆加密简单实现方法
2017/08/25 PHP
微信小程序发送订阅消息的方法(php 为例)
2019/10/30 PHP
JavaScript 学习笔记之一jQuery写法图片等比缩放以及预加载
2012/06/28 Javascript
用Js实现的动态增加表格示例自己写的
2013/10/21 Javascript
js自动生成的元素与页面原有元素发生堆叠的解决方法
2013/10/24 Javascript
javascript实现微信分享
2014/12/23 Javascript
jquery实现LED广告牌旋转系统图片切换效果代码分享
2015/08/26 Javascript
js实现左侧网页tab滑动门效果代码
2015/09/06 Javascript
JS实现刷新父页面不弹出提示框的方法
2016/06/22 Javascript
手机端图片缩放旋转全屏查看PhotoSwipe.js插件实现
2016/08/25 Javascript
jQuery动态增减行的实例代码解析(推荐)
2016/12/05 Javascript
Node做中转服务器转发接口
2017/10/18 Javascript
Vue项目部署在Spring Boot出现页面空白问题的解决方案
2018/11/26 Javascript
js中值引用和地址引用实例分析
2019/06/21 Javascript
vue实现全屏滚动效果(非fullpage.js)
2020/03/07 Javascript
python调用cmd复制文件代码分享
2013/12/27 Python
详解Python 数据库 (sqlite3)应用
2016/12/07 Python
Python决策树和随机森林算法实例详解
2018/01/30 Python
python 多线程将大文件分开下载后在合并的实例
2018/11/09 Python
pandas求两个表格不相交的集合方法
2018/12/08 Python
python set集合使用方法解析
2019/11/05 Python
python orm 框架中sqlalchemy用法实例详解
2020/02/02 Python
pycharm激活码免费分享适用最新pycharm2020.2.3永久激活
2020/11/25 Python
HTML5 WebSocket实现点对点聊天的示例代码
2018/01/31 HTML / CSS
Ruby如何实现动态方法调用
2012/11/18 面试题
新闻系毕业生推荐信
2013/11/16 职场文书
学校关爱留守儿童活动方案
2014/08/27 职场文书
党员个人公开承诺书
2014/08/29 职场文书
证劵公司反洗钱宣传活动总结
2015/05/08 职场文书
导游词之上海杜莎夫人蜡像馆
2019/11/22 职场文书
pytorch fine-tune 预训练的模型操作
2021/06/03 Python
Redis如何实现验证码发送 以及限制每日发送次数
2022/04/18 Redis