spring原始碼解析之原型模式下的迴圈依賴

2021-10-04 19:26:37 字數 2865 閱讀 4758

眾所周知,spring託管了我們物件的建立,銷毀,管理著整個bean的生命週期。但是在物件的建立過程中,有一種特殊情況,存在可能兩個bean之間互相引用,例如下面的testa中引用了testb,testa中引用了testa,即你中有我,我中有你。

public class testa 

******************************==

public class testb

那像這種情況,為什麼會產生迴圈依賴呢,spring又是如何巧妙的解決了這麼乙個問題呢?我們帶著問題繼續看下去。

在spring中,所有的bean預設是單例的,即singleton。而多例模式下的迴圈依賴,spring是無法解決的。首先看下原始碼,這裡有乙個prototypescurrentlyincreation變數,很是重要,spring也給出了說明,這個變數用來記錄當前正在建立的beanname。最後再著重介紹下到底為什麼要設這麼乙個標誌。

/** names of beans that are currently in creation */

private final threadlocalprototypescurrentlyincreation = new namedthreadlocal<>("prototype beans currently in creation");

在剛開始呼叫dogetbean(beanname)來建立bean的時候,會呼叫isprototypecurrentlyincreation方法進行判斷,判斷當前的beanname是否在當前執行緒變數中,如果在則直接丟擲beancurrentlyincreationexception異常。

/*在剛進來建立時,會判斷當前bean是否正在建立*/

if (isprototypecurrentlyincreation(beanname))

protected boolean isprototypecurrentlyincreation(string beanname)

第一次進來建立時,變數中肯定沒有儲存,返回false。接著往下走,呼叫beforeprototypecreation方法,將當前的beanname加入當前執行緒的變數中儲存起來,打上乙個標記,當前的bean已經開始建立了,為以後的迴圈依賴做準備。

//ioc容器建立原型模式bean例項物件

else if (mbd.isprototype())

finally

//獲取給定bean的例項物件

bean = getobjectforbeaninstance(prototypeinstance, name, beanname, mbd);

}protected void beforeprototypecreation(string beanname)

//...省略無關**

}

第二步會呼叫createbean方法,而cratebean的真正是實現是docreatebean,這裡會完成bean的建立,屬性依賴注入,初始化等一系列操作。

//建立bean例項物件

@override

protected object createbean(string beanname, rootbeandefinition mbd, @nullable object args)

throws beancreationexception

catch (beancreationexception ex)

//...省略無關**

}//真正建立bean的方法

protected object docreatebean(final string beanname, final rootbeandefinition mbd, final @nullable object args)

throws beancreationexception

//bean物件的初始化,依賴注入在此觸發

//初始化bean物件

exposedobject = initializebean(beanname, exposedobject, mbd);

//...省略無關**

return exposedobject;

}

在呼叫populatebean方法時,會去為每個屬性賦值,以上面的testa,testb為例,當testa發現依賴testb,接著就會去getbean(testb),testb的建立流程跟testa一樣。首先會先去set集合中獲取當前beanname是否正在建立當中,如果沒有,會進行set操作,儲存到當前執行緒變數中。然後繼續進行bean的建立。當呼叫到populatebean方法時,testb會發現依賴testa,那麼轉過身又會回去建立testa,當呼叫到isprototypecurrentlyincreation方法時,此時的testa由於還在進行屬性賦值,並沒有完全的建立成功,那麼理所當然他會在set集合當中,此時程式會直接丟擲異常,告知當前的bean正在建立當中。

那麼問題來了,如果不拋異常,會怎麼樣呢?沒有這麼乙個set集合又會怎麼樣呢?大家可以試想一下。如果沒有這麼乙個標記,testa在進行屬性賦值時,發現依賴testb,馬上又去建立testb,到testb進行屬性賦值時,又發現依賴了testa,轉過頭又去建立testa,而testa依賴了testb,則又會去建立testb。迴圈往復,形成了乙個死迴圈,永遠都出不來了。所以spring問了避免這種現象,索性直接丟擲異常,因為在spring看來,它也不知道如何來解決這種情況。

Spring原始碼解析之 Aop原始碼解析(2)

spring aop 更多的是oop開發模式的乙個補充,幫助oop以更好的方式來解決對於需要解決業務功能模組之上統一管理 的功能 以一副圖來做為aop功能的說明更直觀些。對於類似系統的安全檢查,系統日誌,事務管理等相關功能,物件導向的開發方法並沒有更好的解決方法 aop引入了一些概念。更多的是spr...

Spring原始碼之XML解析

資料準備階段 準備的目的是封裝 resource引數,目的是為了考慮到 resource可能存在編碼要求的情況,其次,通過 sax讀取 xml檔案的方式來準備 inputsource物件,最後將引數傳遞到 最核心的實現部分 doloadbeandefinitions inputsource,enco...

Spring 原始碼解析之BeanFactory介面

beanfactory介面是spring 容器的根介面,其他介面和類通過對這個介面的實現物件的例項化,通過對該介面的控制,實現對目標物件的例項化。string factory bean prefix 用於取消對beanfactory例項的引用區分factory和其實例,如果是 返回factory,否...