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){}
}
 

你可能感兴趣的:(Java,java)