使用@Lazy解决循环依赖问题

现象

现在有两个Bean,一个叫beanA,一个叫beanBbeanA通过构造器依赖beanBbeanB通过 @Autowrited依赖beanA

  1. BeanA.java
    @Component
    public class BeanA {
    
        private final BeanB beanB;
    
        public BeanA(BeanB beanB) {
            this.beanB = beanB;
        }
    }
    
  2. BeanB.java
    @Component
    public class BeanB {
    
        @Autowired
        private BeanA beanA;
    
    }
    

问题

上面的这两个Bean在启动时会被Spring提示循环依赖,服务启动失败。
在这里插入图片描述

探索循环依赖产生的原因

首先,我们先来探究一下这是一个什么样的循环依赖:

  1. Spring通过AbstractAutowireCapableBeanFactory#createBeanInstance创建BeanA的实例对象
  2. BeanA没有默认的构造器,也没有被@Autowrited的修饰的构造,所以Spring会选择带有BeanB的构造器来实例化BeanA
  3. Spring开始实例化BeanBBeanB直接使用默认构造器实例化
  4. 实例化完成后,开始对BeanB执行AbstractAutowireCapableBeanFactory.populateBean,这时会由AutowiredAnnotationBeanPostProcessor来处理BeanB上的@Autowired注解。
  5. 由于BeanB @Autowired了BeanA,所以此时BeanB需要BeanA的实例来完成自己的依赖注入。
  6. 但是,此时BeanA已经开始实例化,但是还没有实例化完成(因为需要构造器注入BeanB
  7. 所以,就产生了循环依赖

破除循环依赖

那么要如何破除循环依赖呢?常见的办法有两种:

  1. 修改代码逻辑
    1. 从逻辑上进行整改,从而避免循环依赖的产生
    2. 不要使用构造器依赖,至少让bean可以被实例化
  2. 使用代理

使用 @Lazy 其实用的就是代理的方式来解决循环依赖的。

@Lazy解决循环依赖的方式

接下来我们debug看下为什么@Lazy能解决循环依赖

  1. 在创建beanA时,Spring依旧找到了带有BeanB的构造器
    在这里插入图片描述
  2. Spring依然需要实例化一个BeanB出来,但是因为BeanA的构造器上写了 @Lazy,所以SpringBeanB创建了一个代理,并返回了。
    在这里插入图片描述
  3. 接下来BeanB正常进行BeanB的初始化,当处理BeanB@Autowired BeanA时,由于BeanA已经创建完毕,所以BeanB也就可以正常创建了。
  4. 因为BeanA中的beanB其实是一个BeanB的代理,所以 beanA.getBeanB() 一定和beanB不是一个对象
    在这里插入图片描述
  5. 但是 beanA.getBeanB().getBeanA() 却和beanA是同一个对象
    在这里插入图片描述
  6. 这是因为在执行代理对象的getBeanA()时,会触发TargetSourcegetTarget()方法,从而返回真正的beanB
    在这里插入图片描述
    所以beanA.getBeanB().getBeanA()和beanA指向的是同一个对象

总结

  1. 其实在SpringBoot 2.6.0之后Spring官方已经不建议循环依赖了,出现循环依赖还是最好从编码层面做解耦比较好。
  2. 如果项目比较复杂,解耦不易的话,可以采用 @Lazy的方式解决一部分循环依赖的问题。但是要注意 @Lazy会使用代理,用代理有用代理的坑,比如CGLIB代理模式下不能直接访问属性、final和private的方法不能代理等。
  • 14
    点赞
  • 54
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值