Spring中的循环依赖的解决办法
文章目录
- Spring中的循环依赖的解决办法
- 情形一:使用构造注入方式注入依赖
- 情形二:使用Setter方式进行依赖注入
- 情形三:使用延迟加载进行依赖注入
- 情形四:使用第三方库进行依赖注入
先说明:推荐使用构造注入依赖的方式来解决循环依赖。还有不要把实例化与初始化搞混了,注意这两个的时机。
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 {@Overridepublic 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类真正需要使用依赖对象时再进行依赖注入。这样就不会出现循环依赖的问题。
上面就是对循环依赖的解决方式分析,推荐使用构造方式注入。