相信大多數人在學習spring時 ioc 和 bean 算得上是最常聽到的兩個名詞,ioc在學習spring當中出現頻率如此之高必然有其原因。如果我們做一個比喻的話,把bean說成spring中主角的話,那麼ioc便是這個主角進行演出的舞台,沒有ioc作為bean的承載,那麼bean便不會在程式設計中大放異彩。作為spring核心元件的重要一員,了解其内部實作對我們程式設計和窺探spring内幕是相當有幫助的,下面一步步從源碼的角度來剖析ioc究竟是怎樣實作的。
首先我們先通過一張接口設計圖了解下,ioc容器整體的設計架構

通過上面的接口設計圖我們可以看到,在spring當中最基本的ioc容器接口是beanfactory,這個接口當中定義作為一個ioc容器最基本的一些操作和功能,下來看看它的源碼:
通過上面的接口說明我們看到在beanfactory裡面隻對ioc容器最基本的行為做了定義,而不關心bean是怎樣定義和加載的。如果我們想要知道一個工廠具體生産對象的過程,就需要去看這個接口的實作類,在spring當中實作這個接口的有很多子類,下面我們來看看其一個常用的實作類xmlbeanfacotry的代碼吧。
我們可以看到要構造xmlbeanfactory對象的話需要一個resource對象,這個resource是什麼呢?
簡單的講resource接口是為了提供更強的通路底層資源能力的抽象,它是spring通路資源最基本的接口。
通過xmlbeanfactory我們也可以大緻可以猜到,對于該類的要傳的resource對象便是一個xml的檔案流。那麼,這下我們知道這個beanfactory的簡單實作類的用法,也知道了構造這個對象的的參數,先動手實踐建立一個最原始的ioc容器吧。
1、實體類user.java
2、xml資源檔案beans.xml
使用單步調試了解上面代碼的主要内部調用流程,這裡沒有跟蹤getbean()方法的過程,該步驟後設計bean的解析過程,後面我們再說這個
調用順序
類名
方法名
1
xmlbeanfactory
構造方法(resource)
2
defaultlistablebeanfactory
構造方法(beanfactory)
3
abstractautowirecapablebeanfactory
4
setparentfactory(beanfactory)
5
xmlbeandefinitionreader
loadbeandefinitions(resource)
6
abstractbeandefinitionreader
getresourceloader()
通過上面的流程,我們可以簡單的将使用ioc容器的步驟概括如下:
我們對這個内部流程有了了解了以後,下面可以在實踐當中進行應用,我們之前寫的mysimplebeanfactory代碼便可以更改為下面的形式:
兩者的作用是等效的。
我們現在對ioc大體上有了初步了解後,下面對一些關鍵點進行細節上的一些分析。首先,從我們第一步建立的classpathresource對象說起,重新回過頭來認識spring當中的resource。
在上面的文章我們也簡單提到過 resource 接口的作用,其實spring使用自己的抽象結構對整個架構中所用到資源進行統一的封裝,封裝這個接口的原因其實大家也大緻可以想到,我們在程式設計當中所遇到的資源檔案類型多種多樣有file、inputstream、urlresource、contextresource等等,面對如此多的資源類型架構内部要是不設計出一套成熟合理的資源封裝體系的話,無疑對架構的實作和開發者的程式設計來說造成很多的複雜性。面向對象程式設計當中的封裝和繼承特性,在spring當中可謂應用的淋漓盡緻,有時間自己可以花點功夫體會spring架構這一套設計體系對我們的以後編寫可擴充和可用代碼有很大的好處。
繼續展開上面resource接口的介紹,該接口作為資源的一個原始封裝,它繼承自inputstreamresource接口,inputstreamresource隻有<code>inputstream getinputstream()</code>這樣一個方法,通過這次繼承賦予了通過resource對象獲得inputstram流的功能。下面看看接口的源碼:
再看看我們上面代碼當中所用到的resource接口的實作類classpathresource的部分需要重點關注的源碼
我們已經把資源層面上的接口和實作了解了,下面分析我們之前代碼當中的<code>defaultlistablebeanfactory factory = new defaultlistablebeanfactory();</code>這句。在這裡我們有引入了一個新類defaultlistablebeanfactory,從ioc類圖中可以看到這個是beanfactory的一個預設實作類,其執行個體對象可以作為一個獨立使用的ioc容器,可以認為該對象是容納bean對象的一個容器,我們定義好的bean對象經過spring的載入和加載等處理後,最終交由改工廠進行管理,我們需要得到bean對象時便可以向它擷取。
我們調用其無參構造方法建立了factory對象,看看底層都做了哪些事情。
接下來跟蹤父類abstractautowirecapablebeanfactory中源碼
上面我們建立好了 defaultlistablebeanfactory 對象,已經有了容納bean的容器了,但我們通過剛才分析源碼可以看到現在我們還沒有對該factory進行其他實用性的操作,裡面還沒有任何東西。那麼,我們定義在配置檔案定義的bean是如何進入spring容器當中的,就得繼續分析下面的代碼了。我們先分析<code>xmlbeandefinitionreader reader = new xmlbeandefinitionreader(factory);</code>的實作。
從xmlbeandefinitionreader類名中我們又見到一個新名詞beandefinition,我們先了解下spring當中beandefinition的作用。
接着我們看xmlbeandefinitionreader類中關鍵部分的源碼
上面的源碼涉及到了對xml加載的過程,而對于一個xml的加載可以将其分為三步
1.讀取xml檔案的驗證模式,即判讀其使用的是dtd還是xsd
2.依據上面的驗證模式對xml檔案進行驗證
3.加載xml檔案
上面的過程在源碼的展現如下:
通過以上的驗證準備,就可以對xml檔案進行加載了。而加載的過程xmlbeandefinitionreader并沒有自己去完成,而是由documentloader接口去完成的。跟蹤源碼我們發現具體的實作是<code>defaultdocumentloader.loaddocument()</code>,源碼展現如下
接着看defaultdocumentloader中的方法實作
通過這個方法便可以獲得xml的document對象了。
現在我們已經獲得xml解析後的document對象,接下來隻要在對該document結構進行分析便可以知道bean在xml中是如何定義的,也就能将其轉換為beandefinition對象。
這個過程調用的是<code>xmlbeandefinitionreader類的registerbeandefinitions(document doc, resource resource)</code>方法,代碼如下:
上面的過程就不仔細分析了,感興趣的可以自行閱讀beandefinitionparserdelegate的實作,主要邏輯就是根據不同的标簽調用相應的處理流程對完成對beandefinition的解析。這步處理完成了,離我們的目标也就不遠啦。
我們配置的bean的資訊經過解析在spring内部已經轉換為beandefinition這種統一的結構,但這些資料還不能供ioc容器直接使用,需要在ioc容器中對這些beandefinition資料進行注冊。
注冊過程調用的是defaultlistablebeanfactory的<code>registerbeandefinition</code>方法
上面的注冊操作完畢後,一個最簡單的ioc容器就可用使用啦,我們今天分析工作也就完工了。看到這裡是不是有點頭昏腦漲了~(>_<)~,要做到了解spring内部結構并不是一件一朝一夕的事,最好的學習方法就是閱讀了文章對大體流程有了了解後,自己單步去跟蹤學習。最近初步涉獵spring源碼,自己寫的過程中若有不足之處,還望大家及時回報。