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;@Autowiredpublic CircularDependencyA(CircularDependencyB circB) {this.circB = circB;}
}
@Component
public class CircularDependencyB {private CircularDependencyA circA;@Autowiredpublic 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 {@Testpublic 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;@Autowiredpublic CircularDependencyA(@Lazy CircularDependencyB circB) {this.circB = circB;}
}

使用@Lazy后,运行代码,可以看到异常消除。

4.3 使用Setter/Field注入

Spring文档建议的一种方式是使用setter注入。当依赖最终被使用时才进行注入。对前文的样例代码少做修改,来观察测试效果。

@Component
public class CircularDependencyA {private CircularDependencyB circB;@Autowiredpublic void setCircB(CircularDependencyB circB) {this.circB = circB;}public CircularDependencyB getCircB() {return circB;}
}
@Component
public class CircularDependencyB {private CircularDependencyA circA;private String message = "Hi!";@Autowiredpublic void setCircA(CircularDependencyA circA) {this.circA = circA;}public String getMessage() {return message;}
}
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = { TestConfig.class })
public class CircularDependencyTest {@AutowiredApplicationContext context;@Beanpublic CircularDependencyA getCircularDependencyA() {return new CircularDependencyA();}@Beanpublic CircularDependencyB getCircularDependencyB() {return new CircularDependencyB();}@Testpublic void givenCircularDependency_whenSetterInjection_thenItWorks() {CircularDependencyA circA = context.getBean(CircularDependencyA.class);Assert.assertEquals("Hi!", circA.getCircB().getMessage());}
}

4.4 使用@PostConstruct

@Component
public class CircularDependencyA {@Autowiredprivate CircularDependencyB circB;@PostConstructpublic 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;}@Overridepublic void afterPropertiesSet() throws Exception {circB = context.getBean(CircularDependencyB.class);}@Overridepublic void setApplicationContext(final ApplicationContext ctx) throws BeansException {context = ctx;}
}
@Component
public class CircularDependencyB {private CircularDependencyA circA;private String message = "Hi!";@Autowiredpublic void setCircA(CircularDependencyA circA) {this.circA = circA;}public String getMessage() {return message;}
}

5.总结

处理循环依赖有多种方式。首先考虑是否能够通过重新设计依赖来避免循环依赖。如果确实需要循环依赖,那么可以通过前文提到的方式来处理。优先建议使用setter注入来解决。

SpringBoot构造器注入循环依赖及解决相关推荐

  1. Spring构造器注入循环依赖的解决方案及原理探索

    前言 我们都知道Spring解决了Setter注入或者Field注入的循环依赖问题,依靠的是三个Map(earlySingletonObjects.singletonFactories.singlet ...

  2. Spring-bean的循环依赖以及解决方式___Spring源码初探--Bean的初始化-循环依赖的解决

    本文主要是分析Spring bean的循环依赖,以及Spring的解决方式. 通过这种解决方式,我们可以应用在我们实际开发项目中. 什么是循环依赖? 怎么检测循环依赖 Spring怎么解决循环依赖 S ...

  3. Spring循环依赖及其解决方式

    部分原文链接:java 循环依赖_Java详解之Spring Bean的循环依赖解决方案_以太创服的博客-CSDN博客 1,什么是循环依赖: 在spring中,对象的创建是交给Spring容器去执行的 ...

  4. spring循环依赖及解决方法

    一.三种循环依赖的情况 ①构造器的循环依赖:这种依赖spring是处理不了的,直接抛出BeanCurrentlylnCreationException异常. ②单例模式下的setter循环依赖:通过& ...

  5. map 循环_被问到Spring循环依赖怎么解决?秀给面试官看!内附图解

    不知道最近有没有被一道Java面试题刷爆朋友圈,Spring框架的循环依赖如何解决.我收到了不少粉丝的提问,在了解到之后,也去网上查询了一些资料,自己也询问了身边的同事,总结出以下几个方面,今天就和我 ...

  6. spring循环依赖及解决方式_来探究一下Spring 循环依赖的三种方式

    引言:循环依赖就是N个类中循环嵌套引用,如果在日常开发中我们用new 对象的方式发生这种循环依赖的话程序会在运行时一直循环调用,直至内存溢出报错.下面说一下Spring是如果解决循环依赖的. 第一种: ...

  7. spring相互依赖怎么解决_被问到Spring循环依赖怎么解决?秀给面试官看!内附图解...

    不知道最近有没有被一道Java面试题刷爆朋友圈,Spring框架的循环依赖如何解决.我收到了不少粉丝的提问,在了解到之后,也去网上查询了一些资料,自己也询问了身边的同事,总结出以下几个方面,今天就和我 ...

  8. Spring中循环依赖的解决办法

    点击上方蓝色字体,选择"标星公众号" 优质文章,第一时间送达 1.什么是循环依赖? 循环依赖就是循环引用,就是两个或多个bean相互之间的持有对方. A类中有一个B类型的成员变量, ...

  9. 【Spring面试题】循环依赖如何解决?

    1.什么是循环依赖 循环依赖指的是两个类中的属性相互依赖对方:例如 A 类中有 B 属性,B 类中有 A属性,从而形成了一个依赖闭环. 2.循环依赖的两种情况及其解决方案 2.1 构造器的循环依赖 这 ...

最新文章

  1. python async await报错_Python 3.7.7 发布 支持async并await现在为保留关键字
  2. android 保存流媒体,Android实现使用流媒体播放远程mp3文件的方法
  3. 编译器会影响编译吗?
  4. 如何关闭Windows10任务栏里的应用图标
  5. 苹果CMS小俊XG013主题模板源码
  6. HTML img 标签的 border 属性
  7. SQL注入学习part01:(结合sqli-libs学习:1-10关)
  8. [Python]从哪里开始学习写代码(未完待续)
  9. LintCode2016年8月8日算法比赛----子树
  10. java获取classes_一个Java项目布署到weblogic里,听说weblogic会把classes目录打成jar包,怎么获取classes里文件的路径...
  11. Zabbix 网页端监控工具
  12. php展厅控制系统,展厅中控系统
  13. NoteFirst的Word插件(WordAddIn)安装过程中遇到的问题和解决方案
  14. 测试工具Monitor入门
  15. 微信HTML5页面设计建议
  16. 4005. 取石子游戏
  17. 小程序获取上一个页面或者某个页面内的值
  18. 华为手机安装Goole play教程及安装包
  19. mysql分页查询出租房屋信息_分页查询信息(使用jdbc连接mysql数据库实现分页查询任务)...
  20. oracle 10g与oracle 11g的不同

热门文章

  1. Easyui datagrid detailview使用
  2. 如何去除照片中的水印
  3. cmd 关闭计算机,cmd shutdown命令,立即关机命令shutdown
  4. android surfaceview 大小,Android设置SurfaceView任意大小、任意位置、保持预览宽高比与屏...
  5. 非常精美的纸艺术作品欣赏(下篇)
  6. HP LaserJet Pro P1606dn 激光打印机 - 配置 IP 地址
  7. Android 手机各大厂商的指纹设置界面
  8. 最优化的非线性例子集中
  9. 微信 html tree,微信小程序 tree组件
  10. Hololens官方第三视角技术