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類真正需要使用依賴對象時再進行依賴注入。這樣就不會出現循環依賴的問題。
上面就是對循環依賴的解決方式分析,推薦使用構造方式注入。