Springboot循环依赖如何解决

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.普通注入之间的循环依赖

比如:我现在有一个ServiceA需要调用ServiceB的方法,那么ServiceA就依赖于ServiceB,那在ServiceB中再调用ServiceA的方法,就形成了循环依赖。Spring在初始化bean的时候就不知道先初始化哪个,bean就会报错。


 
 
  1. public class ClassA {
  2. @Autowired
  3. ClassB classB;
  4. }
  5. public class ClassB {
  6. @Autowired
  7. ClassA classA
  8. }
如何解决循环依赖,最好的方法是重构代码,进行解耦,如果没有时间重构,可以使用下面的方法: 

(1)在你的配置文件中,在互相依赖的两个bean的任意一个加上lazy-init属性


 
 
  1. <bean id= "ServiceDependent1" class= "org.xyz.ServiceDependent1" lazy-init= "true"
  2. <constructor-arg ref= "Service"/> </bean>  
  3.  <bean id= "ServiceDependent2" class= "org.xyz.ServiceDependent2" lazy-init= "true"
  4. <constructor-arg ref= "Service"/> </bean>   

  (2)在你注入bean时,在互相依赖的两个bean上加上@Lazy注解也可以


 
 
  1. @Autowired     
  2. @Lazy      
  3. private ClassA classA; 
  4. @Autowired 
  5. @Lazy      
  6. private ClassB classB; 

4. 构造器注入循环依赖实例

首先定义两个相互通过构造器注入依赖的bean。


 
 
  1. @Component
  2. public class CircularDependencyA {
  3. private CircularDependencyB circB;
  4. @Autowired
  5. public CircularDependencyA(CircularDependencyB circB) {
  6. this.circB = circB;
  7. }
  8. }

 
 
  1. @Component
  2. public class CircularDependencyB {
  3. private CircularDependencyA circA;
  4. @Autowired
  5. public CircularDependencyB(CircularDependencyA circA) {
  6. this.circA = circA;
  7. }
  8. }

 
 
  1. @Configuration
  2. @ComponentScan(basePackages = { "com.baeldung.circulardependency" })
  3. public class TestConfig {
  4. }

 
 
  1. @RunWith(SpringJUnit4ClassRunner.class)
  2. @ContextConfiguration(classes = { TestConfig.class })
  3. public class CircularDependencyTest {
  4. @Test
  5. public void givenCircularDependency_whenConstructorInjection_thenItFails() {
  6. // Empty test; we just want the context to load
  7. }
  8. }

运行方法givenCircularDependency_whenConstructorInjection_thenItFails将会产生异常:BeanCurrentlyInCreationException: Error creating bean with name ‘circularDependencyA’:
Requested bean is currently in creation: Is there an unresolvable circular reference?

如何解决

(1)重新设计

重新设计结构,消除循环依赖。

(2)使用注解 @Lazy

一种最简单的消除循环依赖的方式是通过延迟加载。在注入依赖时,先注入代理对象,当首次使用时再创建对象完成注入。


 
 
  1. @Component
  2. public class CircularDependencyA {
  3. private CircularDependencyB circB;
  4. @Autowired
  5. public CircularDependencyA(@Lazy CircularDependencyB circB) {
  6. this.circB = circB;
  7. }
  8. }

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

(3)使用Setter/Field注入

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


 
 
  1. @Component
  2. public class CircularDependencyA {
  3. private CircularDependencyB circB;
  4. @Autowired
  5. public void setCircB(CircularDependencyB circB) {
  6. this.circB = circB;
  7. }
  8. public CircularDependencyB getCircB() {
  9. return circB;
  10. }
  11. }

 
 
  1. @Component
  2. public class CircularDependencyB {
  3. private CircularDependencyA circA;
  4. private String message = "Hi!";
  5. @Autowired
  6. public void setCircA(CircularDependencyA circA) {
  7. this.circA = circA;
  8. }
  9. public String getMessage() {
  10. return message;
  11. }
  12. }

 
 
  1. @RunWith(SpringJUnit4ClassRunner.class)
  2. @ContextConfiguration(classes = { TestConfig.class })
  3. public class CircularDependencyTest {
  4. @Autowired
  5. ApplicationContext context;
  6. @Bean
  7. public CircularDependencyA getCircularDependencyA() {
  8. return new CircularDependencyA();
  9. }
  10. @Bean
  11. public CircularDependencyB getCircularDependencyB() {
  12. return new CircularDependencyB();
  13. }
  14. @Test
  15. public void givenCircularDependency_whenSetterInjection_thenItWorks() {
  16. CircularDependencyA circA = context.getBean(CircularDependencyA.class);
  17. Assert.assertEquals( "Hi!", circA.getCircB().getMessage());
  18. }
  19. }

(4) 使用@PostConstruct


 
 
  1. @Component
  2. public class CircularDependencyA {
  3. @Autowired
  4. private CircularDependencyB circB;
  5. @PostConstruct
  6. public void init() {
  7. circB.setCircA( this);
  8. }
  9. public CircularDependencyB getCircB() {
  10. return circB;
  11. }
  12. }

 
 
  1. @Component
  2. public class CircularDependencyB {
  3. private CircularDependencyA circA;
  4. private String message = "Hi!";
  5. public void setCircA(CircularDependencyA circA) {
  6. this.circA = circA;
  7. }
  8. public String getMessage() {
  9. return message;
  10. }

(5)实现ApplicationContextAware与InitializingBean


 
 
  1. @Component
  2. public class CircularDependencyA implements ApplicationContextAware, InitializingBean {
  3. private CircularDependencyB circB;
  4. private ApplicationContext context;
  5. public CircularDependencyB getCircB() {
  6. return circB;
  7. }
  8. @Override
  9. public void afterPropertiesSet() throws Exception {
  10. circB = context.getBean(CircularDependencyB.class);
  11. }
  12. @Override
  13. public void setApplicationContext(final ApplicationContext ctx) throws BeansException {
  14. context = ctx;
  15. }
  16. }

 
 
  1. @Component
  2. public class CircularDependencyB {
  3. private CircularDependencyA circA;
  4. private String message = "Hi!";
  5. @Autowired
  6. public void setCircA(CircularDependencyA circA) {
  7. this.circA = circA;
  8. }
  9. public String getMessage() {
  10. return message;
  11. }
  12. }

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值