Bug解决:SpringBoot循环依赖注入的问题

目录

项目场景:

问题描述:

原因分析:

解决方案:

一、改为使用 Setter 方式注入

二、使用 @Lazy 注解

三、使用 PostConstruct 注解


项目场景:

后台架构为SpringBoot,业务上有一套调第三方接口逻辑,由于不稳定,需要另写一套新的。旧的有部分接口要依赖新的逻辑,新的有一部分需要通过旧的去获取第三方数据。于是,导致了代码上这两个类互相依赖,一启动直接裂开,报循环依赖错误。

后来查阅了资料,了解了循环依赖注入的原理,在不解耦的情况下(当然最好的方式是解耦,重新设计),也有了几种解决方案,最后空闲之余,写了这篇博客来总结下这次有意思的体验。


问题描述:

问题出现很明显,两个类互相依赖注入,这种情况下Spring不知道先创建哪个bean,

就会抛出BeanCurrentlyInCreationException错误,如下


Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'classA': Requested bean is currently in creation: Is there an unresolvable circular reference?
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.beforeSingletonCreation(DefaultSingletonBeanRegistry.java:347)
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:219)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:321)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
	at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:276)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1304)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1224)
	at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:884)
	at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:788)
	... 57 more

 


原因分析:

首先说一下什么是循环依赖注入,简单举个例子,比如有两个类,ClassA 和 ClassB。其中,ClassA 中通过依赖注入了 ClassB,ClassB中也通过依赖注入了 ClassA,此时ClassA和ClassB互相依赖,成为闭环,就会导致异常产生。

但是其实Spring是可以有策略可以解决循环依赖的,不过如果是通过构造器注入的方式,Spring无法解决。如果是setter方法注入则可以解决此问题,后面再说。这里先写个小demo来重现这个问题。如下

ClassA


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * 测试循环依赖问题.
 *
 * A类
 *
 * @author linzp
 * @version 1.0.0
 * CreateDate 2020/10/12 19:15
 */
@Service
public class ClassA {

    /**
     * 依赖classB.
     */
    private ClassB classB;

    @Autowired
    public ClassA(ClassB classB){
        this.classB = classB;
    }

    public void test(){

    }
}

ClassB 


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * 测试循环依赖问题.
 *
 * B类
 *
 * @author linzp
 * @version 1.0.0
 * CreateDate 2020/10/12 19:18
 */
@Service
public class ClassB {

    /**
     * 依赖classA.
     */
    private ClassA classA;

    @Autowired
    public ClassB(ClassA classA){
        this.classA = classA;
    }
}

 如果在这时启动项目,就会启动失败,结果如下图。

如果编写单元测试来调用ClassA的方法的话,则会抛如上的 BeanCurrentlyInCreationException 异常。

注:这里两个类都是基于构造器注入的。


解决方案:

解决的方案有很多种。但是,如果在可能的情况下,重新设计和解耦 是推荐的办法。如果不方便重构代码,也可以使用其他方式来解决。

一、改为使用 Setter 方式注入

这是Spring官方推荐的做法。通过使用构造器注入可以解决问题。更改后的两个类代码如下:

@Service
public class ClassA {

    /**
     * 依赖classB.
     */
    private ClassB classB;

    /**
     * 使用Setter方式注入ClassB
     * @param classB
     */
    @Autowired
    public void setClassB(ClassB classB){
        this.classB = classB;
    }

    public void test(){

    }
}
@Service
public class ClassB {

    /**
     * 依赖classA.
     */
    private ClassA classA;

    /**
     * 使用Setter方式注入ClassA
     * @param classA
     */
    @Autowired
    public void setClassA(ClassA classA){
        this.classA = classA;
    }
}

二、使用 @Lazy 注解

 通过在其中一个或两个类的注入属性或方法中,添加 @Lazy注解,一般亦可解决问题

@Service
public class ClassA {

    /**
     * 依赖classB.
     */
    private ClassB classB;

    /**
     * 使用构造器方式注入ClassB
     * @param classB
     */
    @Autowired
    @Lazy  //添加@Lazy注解
    public ClassA(ClassB classB){
        this.classB = classB;
    }

    public void test(){ }
}
@Service
public class ClassB {

    /**
     * 依赖classA.
     */
    private ClassA classA;

    /**
     * 使用构造器方式注入ClassA
     * @param classA
     */
    @Autowired
    @Lazy //添加@Lazy注解
    public ClassB(ClassA classA){
        this.classA = classA;
    }
}

三、使用 PostConstruct 注解

@Service
public class ClassA {

    /**
     * 依赖classB.
     */
    private ClassB classB;

    /**
     * 使用构造器方式注入ClassB
     * @param classB
     */
    @Autowired
    public ClassA(ClassB classB){
        this.classB = classB;
    }

    @PostConstruct
    public void init(){
        classB.setClassA(this);
    }

    public void test(){ }
}

其中,ClassB中的ClassA属性,由set方法来设置,这时候可以看到在ClassB中并没有使用注入的方式。

@Service
public class ClassB {

    /**
     * 依赖classA.
     */
    private ClassA classA;

    public void setClassA(ClassA classA){}
}

 

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

林志鹏JAVA

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值