Solutions to circular dependencies in Spring

Solutions to circular dependencies in Spring

Let me explain first: It is recommended to use constructor injection dependency to solve circular dependencies. Also, don’t confuse instantiation with initialization, pay attention to the timing of these two.

The solutions to circular dependencies in Spring mainly include the following:

Scenario 1: Inject dependencies using constructor injection

  • constructor injection

The reason why constructor injection does not cause cyclic dependencies is that when Spring creates a bean instance, it immediately resolves the dependency and injects the dependent object into the bean instance. For example, in the following code, there is a circular dependency between class A and class 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;
    }
}

When creating an instance of B during constructor injection, you also need to create an instance of A first. However, Spring uses a special mechanism to solve the problem of circular dependencies when creating bean instances.

When Spring creates a bean instance, it will useCircular Dependency Resolver (Circular Dependency Resolver) to solve the problem of circular dependencies. The circular dependency resolver creates bean instances in a certain order. For constructor injection, the circular dependency resolver creates bean instances in the following order:

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

The process is roughly like this, and there will be no problem of circular dependencies.

Scenario 2: Using Setter for dependency injection

  • setter injection

In contrast, setter method injection resolves dependencies after the bean instance is created and injects the dependent objects into the bean instance. For example, in the following code, there is also a circular dependency between class A and class 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;
    }
}

When Spring creates an instance of class A, the instance of class A has already been created. However, the instance of class B has not been created yet. Spring will resolve dependencies and inject instances of class B into class A when calling the setB method. However, since the instance of class A has been created, Spring cannot create an instance of class B. In this way, the problem of circular dependency arises.

The approximate process is as follows:

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

Therefore, it is recommended to use constructor injection if you want to avoid circular dependencies.

Scenario 3: Dependency injection using lazy loading

  • Lazy loading

Lazy loading refers to dependency injection when the bean instance actually needs to use the dependent object. Lazy loading can avoid the problem of circular dependencies, but may cause performance degradation.

Example

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;
    }
}

In the above code, there is a circular dependency between class A and class B. If you use lazy loading, Spring will perform dependency injection only when Class A or Class B really needs to use the dependent object. This way there will be no circular dependency issues

Scenario 4: Using third-party libraries for dependency injection

  • Use third-party libraries

Spring Boot provides the @AutowiredAnnotationBeanPostProcessor class, which can be used to solve the problem of circular dependencies. This class will resolve dependencies after the bean instance is created and inject the dependent objects into the bean instance.

Example

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;
    }
}

In the above code, there is a circular dependency between class A and class B. If you use the @AutowiredAnnotationBeanPostProcessor class, Spring will perform dependency injection when Class A or Class B really needs to use the dependent object. This way there will be no circular dependency issues.

The above is an analysis of the solution to circular dependencies. It is recommended to use constructor injection.

Guess you like

Origin blog.csdn.net/qq_45925197/article/details/134532866
Recommended