【SpringBoot】Bean的注入方式不同导致偶现循环依赖报错

问题描述

最近做项目时,遇到一个很诡异的问题:同样一份代码,在测试环境可以正常启动,但在生产环境却死活起不来,SpringBoot提示循环依赖。按理说,如果有循环依赖问题,测试环境就应该暴露出来了,为什么到生产环境才出现这个问题呢?对此做了一番初步研究。

原因分析

经排查发现,项目中确实存在循环依赖,如下所示。

程序中存在2个Bean:a和b。a构造器注入b,b属性注入a,两者形成循环依赖。

@Component
public class A {

    private final B b;

    @Autowired
    public A(B b) {
        this.b = b;
    }
}
@Component
public class B {

    @Autowired
    private A a;
}

是否会触发循环依赖报错,与Bean的加载顺序有关:

  • 如果先加载b,再加载a,可以正常启动;
  • 如果先加载a,再加载b,则会启动失败。

这个加载顺序跟程序所处的系统环境有关,不是固定的(默认情况下,Spring不保证Bean的加载顺序)。因此会出现,测试环境正常启动、生产环境启动失败的情况。

至于为什么先加载b再加载a可以正常启动、反之则不行,与Spring应对循环依赖问题的三级缓存机制有关:

  • 由于b是属性注入a,如果b先实例化,b在实例化后可以放入缓存,a实例化时从缓存中获取到b,a和b可以顺利完成初始化;
  • 而a是构造器注入b,如果a先实例化,a在实例化构造时要求b已经初始化完成,而b的初始化又依赖于a的实例化,此时a尚未实例化,也就无法从缓存中获取到,这样b和a都无法完成初始化。

解决办法

解决办法有很多,这里列出一些:

  • 在类A上标注@DependsOn("b")注解,强制指定b优先于a加载。
  • 在类A上标注@Lazy注解,让a懒加载。
  • a和b都改成属性注入,让Spring通过三级缓存机制化解循环依赖问题。
  • 重构类A和类B,让它们职责单一,避免循环依赖。

总结

SpringBoot中,Bean的注入方式不同,可能导致“偶现”的循环依赖报错。这里的“偶现”,是针对不同的系统环境而言,由于Bean的加载顺序不同,循环依赖报错可能出现,也可能不出现。为了避免类似的问题,程序开发时最好采用统一的依赖注入方式(建议构造器注入)。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值