基于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与sqlite3实现解密chrome cookie实例代码
Jan 20 Python
python爬虫爬取某站上海租房图片
Feb 04 Python
python matlibplot绘制3D图形
Jul 02 Python
使用Django连接Mysql数据库步骤
Jan 15 Python
Python实现查找字符串数组最长公共前缀示例
Mar 27 Python
简单了解python中对象的取反运算符
Jul 01 Python
Python 分享10个PyCharm技巧
Jul 13 Python
python 视频逐帧保存为图片的完整实例
Dec 10 Python
Django REST 异常处理详解
Jul 15 Python
python利用线程实现多任务
Sep 18 Python
Django Auth用户认证组件实现代码
Oct 13 Python
解决pytorch 模型复制的一些问题
Mar 03 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
做个自己站内搜索引擎
2006/10/09 PHP
PHP 简单数组排序实现代码
2009/08/05 PHP
php绝对路径与相对路径之间关系的的分析
2010/03/03 PHP
基于curl数据采集之单页面采集函数get_html的使用
2013/04/28 PHP
PHP读取大文件末尾N行的高效方法推荐
2016/06/03 PHP
php libevent 功能与使用方法详解
2020/03/04 PHP
ExtJs中简单的登录界面制作方法
2010/08/19 Javascript
入门基础学习 ExtJS笔记(一)
2010/11/11 Javascript
JS的replace方法详细介绍
2012/11/09 Javascript
JS常用函数使用指南
2014/11/23 Javascript
项目中常用的JS方法整理
2015/01/30 Javascript
js选项卡的实现方法
2015/02/09 Javascript
javascript实现确定和取消提示框效果
2015/07/10 Javascript
JavaScript中日期的相关操作方法总结
2015/10/24 Javascript
Ionic3 UI组件之autocomplete详解
2017/06/08 Javascript
20170918 前端开发周报之JS前端开发必看
2017/09/18 Javascript
vue使用axios上传文件(FormData)的方法
2019/04/14 Javascript
vue递归组件实战之简单树形控件实例代码
2019/08/27 Javascript
js实现数字从零慢慢增加到指定数字示例
2019/11/07 Javascript
[09:34]2018DOTA2国际邀请赛寻真——永不放弃的iG
2018/08/14 DOTA
[51:10]VP vs VGJ.S 2018国际邀请赛小组赛BO2 第二场 8.19
2018/08/21 DOTA
详解python实现读取邮件数据并下载附件的实例
2017/08/03 Python
python opencv 直方图反向投影的方法
2018/02/24 Python
Python编写一个优美的下载器
2018/04/15 Python
Python try except异常捕获机制原理解析
2020/04/18 Python
家用个人磨皮机:Trophy Skin
2017/03/30 全球购物
Infababy英国:婴儿推车、Travel System婴儿车和婴儿汽车座椅销售
2018/05/23 全球购物
美国社交购物市场:MassGenie
2019/02/18 全球购物
Linux开机引导的步骤是什么
2015/10/19 面试题
物流专业毕业生推荐信范文
2013/11/18 职场文书
小学信息技术教学反思
2014/02/10 职场文书
化工专业求职信
2014/07/01 职场文书
村党的群众路线教育实践活动工作总结
2014/10/25 职场文书
教师党员个人总结
2015/02/10 职场文书
2015员工年度考核评语
2015/03/25 职场文书
会议主持人开场白台词
2015/05/28 职场文书