研究一下Spring里面的源碼,循環依賴你會么?
前幾天在阿粉CSDN上看一個文章,因為一個Spring的問題,期望薪資三萬卻被生生的壓榨成了兩萬五,高于兩萬五人家都不要,讓我感覺到了Spring的強大,不學習Spring是會吃虧的,那么我們就從各種高頻面試來一點點深入吧。
本文先從一個高頻面試題開始說起吧!阿粉在接下來會給大家帶來各種關于Spring的知識,希望大家能夠一起討論一下呦
Spring是怎么去解決循環依賴的
1.什么是循環依賴
這個詞,阿粉聽到的時候,肯定和大家的反應一樣的,循環,依賴,那是不是 A 引用了 B ,而此時 B 引用了 C,而 C 呢又引用了A,于是一個三角戀的關系出現了。
那么用代碼來表示的話,是怎么表示的呢?
- public class ClassTestA {
- private ClassTestB classTestB;
- public void a(){
- classTestB.b();
- }
- public ClassTestB getClassTestB() {
- return classTestB;
- }
- private void setClassTestB(ClassTestB classTestB){
- this.classTestB = classTestB;
- }
- }
- public class ClassTestB {
- private ClassTestC classTestC;
- public void b(){
- classTestC.c();
- }
- public ClassTestC getClassTestC() {
- return classTestC;
- }
- private void setClassTestC(ClassTestC classTestC){
- this.classTestC = classTestC;
- }
- }
- public class ClassTestC {
- private ClassTestA classTestA;
- public void c(){
- classTestA.a();
- }
- public ClassTestA getClassTestA() {
- return classTestA;
- }
- private void setClassTestA(ClassTestA classTestA){
- this.classTestA = classTestA;
- }
- }
2.循環依賴會出現什么問題
在阿粉的印象中,循環依賴最直接的問題就是會出現在對象的實例化上面,創建對象的時候,如果在Spring的配置中加入這種 A 依賴 B ,B 依賴 C,C 依賴 A 的話,那么最終創建 A 的實例對象的時候,會出現錯誤。
而如果這種循環調用的依賴不去終結掉他的話,那么就相當于一個死循環,就像阿粉前幾天的在維護那個 “十六年”之前的項目的時候,各種內存溢出,表示內心很壓抑呀。
而 Spring 中也將循環依賴的處理分成了不同的幾種情況,阿粉帶大家來看一下吧。
3.Spring循環依賴處理一 (構造器循環依賴)
構造器循環依賴的意思就是說,通過構造及注入構成的循環依賴,而這種依賴的話,是沒有辦法解決的,如果你敢強行依賴,不要意思,出現了你久違的異常 BeanCurrentlyInCreationException 出現這個異常的時候,就是表示循環依賴的問題。
相信大家出現異常的時候,在看不懂為什么的時候,第一時間,復制異常信息,放在百度,或者Google上面查詢一下,BeanCurrentlyInCreationException 放在百度上,一目了然。
而在 Spring 的配置文件中,如果這么配置 A ,B ,C 的循環依賴的時候,在創建 A 的時候,發現,構造器需要 B 類,然后去創建 B ,而創建 B 的時候,發現又需要 C ,然后去創建 C ,創建的時候發現,竟然需要 A ,于是又掉頭回去了,于是就形成了一個閉環,沒有辦法創建。
- <beans>
- <bean id="ClassTestA" class="com.yldlsy.ClassTestA" >
- <constructor-arg index="0" ref="ClassTestB" />
- </bean>
- <bean id="ClassTestB" class="com.yldlsy.ClassTestB" >
- <constructor-arg index="0" ref="ClassTestC" />
- </bean>
- <bean id="ClassTestC" class="com.yldlsy.ClassTestC" >
- <constructor-arg index="0" ref="ClassTestA" />
- </bean>
- </beans>
而在這種情況下,Spring實例化bean是通過ApplicationContext.getBean()方法來進行的。如果要獲取的對象依賴了另一個對象,那么其首先會創建當前對象,然后通過遞歸的調用ApplicationContext.getBean()方法來獲取所依賴的對象,最后將獲取到的對象注入到當前對象中。而和剛才阿粉說的一樣,創建了閉環,所以就沒有辦法創建了。
4.Spring循環依賴處理二(setter循環依賴)
setter循環注入是指通過setter注入方式構成的循環依賴。而這種方式,是Spring可以進行解決的。
而對于這種使用setter注入造成的依賴是通過Spring容器來提前暴露剛完成的構造注入器的bean來完成的,但是這時候還沒有完成其他的步驟的時候。
這個時候我們就需要提前暴露出來一個單例的工廠方法,讓其他的bean來引用這個bean,
- addSingletonFactory(beanName,new ObjectFactory(){
- public Object getObject() throws BeanException{
- return getEarlyBeanReference(beanName,mbd,bean)
- }
- })
- Spring在創建 A 的時候,根據無參構造來創建 A,并且暴露出 ObjectFactory 用來返回一個提前暴露好的 bean 然后再進行setter來注入,
同理的B和C都是這個樣子的,這個時候就能完成setter注入了。
5.Spring循環依賴處理三(作用域循環依賴)
阿粉帶大家看一下這個配置方式
- <bean id="ClassTestA" class="com.yldlsy.ClassTestA" scope="singleton" >
- <property name="ClassTestB" ref="ClassTestB" />
- </bean>
- <bean id="ClassTestA" class="com.yldlsy.ClassTestA" scope="singleton" >
- <property name="ClassTestB" ref="ClassTestB" />
- </bean>
- <bean id="ClassTestA" class="com.yldlsy.ClassTestA" scope="singleton" >
- <property name="ClassTestB" ref="ClassTestB" />
- </bean>
而對于 “singleton”作用域的話,他是可以通過“setAllowCircularReference(false)”這種方式來進制循環依賴的。
而且也是有缺陷的,這種方式只能解決單例作用域的bean循環依賴。
而Spring解決循環依賴的話,大家肯定會好奇,說是三級緩存,那么請找到你的Spring的源碼
- org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean
里面代碼之前阿粉就說過,太多了,給大家發一下阿粉之前的鏈接,里面有詳細介紹Bean加載的所有過程。
文獻參考《Spring源碼深度解析》