Spring中的循环依赖的解决办法

Spring中的循环依赖的解决办法

先说明:推荐使用构造注入依赖的方式来解决循环依赖。还有不要把实例化与初始化搞混了,注意这两个的时机。

Spring中循环依赖的解决方案主要有以下几种:

情形一:使用构造注入方式注入依赖

  • 构造注入

构造注入不会循环依赖的原因在于,Spring在创建bean实例时,会立即解析依赖关系,并将依赖对象注入到bean实例中。例如,以下代码中,A类和B类之间存在循环依赖:

public class A {
    
    

    private B b;
    //步骤1.创建A的实例,未初始化 
    //步骤3获取到B的实例并对A进行初始化
    public A(B b) {
    
    
        this.b = b;
    }
}

public class B {
    
    

    private A a;
    //步骤2创建B的实例未初始化 
    //步骤4获取到A的实例并对B进行初始化
    public B(A a) {
    
    
        this.a = a;
    }
}

构造注入的时候创建B的实例也需要先创建A的实例。但是,Spring在创建bean实例时,会使用一种特殊的机制来解决循环依赖的问题。

Spring在创建bean实例时,会使用循环依赖解析器(Circular Dependency Resolver)来解决循环依赖的问题。循环依赖解析器会按照一定的顺序来创建bean实例。对于构造注入,循环依赖解析器会按照如下顺序来创建bean实例:

1. 创建A的实例,但是A的实例还没有初始化。
2. 创建B的实例,但是B的实例还没有初始化。
3. 初始化A的实例,此时A的实例可以访问到B的实例。
4. 初始化B的实例,此时B的实例可以访问到A的实例。

流程大概是这样,就不会出现循环依赖的问题了。

情形二:使用Setter方式进行依赖注入

  • setter注入

相比之下,setter方法注入是在bean实例创建完成后再解析依赖关系,并将依赖对象注入到bean实例中。例如,以下代码中,A类和B类之间也存在循环依赖:

Java

public class A {

    private B b;
    //步骤1.他是先创建A的实例再创建B的实例(在调用A的Set方法之前肯定是已经有了A的实例) 
    //步骤3.将B实例注入到A就会报错,因为A实例已经存在了
    public void setB(B b) {
        this.b = b;
    }
}

public class B {

    private A a;
    //步骤2.在创建B的实例的时候A的实例已经存在了,可以直接获取到B的实例
    public void setA(A a) {
        this.a = a;
    }
}

当Spring创建A类的实例时,A类的实例已经创建完成了。但是,B类的实例还没有创建。Spring会在调用setB方法时,解析依赖关系,并将B类的实例注入到A类中。但是,由于A类的实例已经创建完成了,所以Spring无法创建B类的实例。这样,就出现了循环依赖的问题。

大概过程如下:

1. 创建A的实例
2. 将B注入到A中
3. 创建B的实例

因此,如果要避免循环依赖,建议使用构造注入。

情形三:使用延迟加载进行依赖注入

  • 延迟加载

延迟加载是指在bean实例真正需要使用依赖对象时才进行依赖注入。延迟加载可以避免循环依赖的问题,但可能会导致性能下降。

示例

Java

@Lazy
public class A {

    private B b;

    public A() {
    }

    public B getB() {
        if (b == null) {
            b = new B();
        }
        return b;
    }
}

public class B {

    private A a;

    public B() {
    }

    public A getA() {
        if (a == null) {
            a = new A();
        }
        return a;
    }
}

上述代码中,A类和B类之间存在循环依赖。如果使用延迟加载,Spring会在A类或B类真正需要使用依赖对象时才进行依赖注入。这样就不会出现循环依赖的问题

情形四:使用第三方库进行依赖注入

  • 使用第三方库

Spring Boot提供了@AutowiredAnnotationBeanPostProcessor类,可以用于解决循环依赖的问题。该类会在bean实例创建完成后再解析依赖关系,并将依赖对象注入到bean实例中。

示例

Java

@AutowiredAnnotationBeanPostProcessor
public class MyBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if (bean.getClass().getName().equals("com.example.A")) {
            A a = (A) bean;
            a.setB(new B());
        }
        return bean;
    }
}

上述代码中,A类和B类之间存在循环依赖。如果使用@AutowiredAnnotationBeanPostProcessor类,Spring会在A类或B类真正需要使用依赖对象时再进行依赖注入。这样就不会出现循环依赖的问题。

上面就是对循环依赖的解决方式分析,推荐使用构造方式注入。

猜你喜欢

转载自blog.csdn.net/qq_45925197/article/details/134532866