一、前言
這幾天正在複習Spring
的相關內容,同時想要對Spring
的實現原理做一些深入的研究。今天看了看Spring
中IoC
的實現,找到了一篇非常詳細的部落格,研究了一個下午,看完之後唯一的感受就是——太複雜了。Spring
原始碼中,類和介面的體系非常的複雜,同時方法的實現也是,方法呼叫感覺無窮無盡,甚至相互呼叫,給我繞的暈暈的。應該是自己目前的技術水平還太低,專案經驗也不足,甚至對Spring
的運用都不夠熟悉,所以研究原始碼對我來說可能還是太早了。
雖然對於Spring
的IoC
,我可能連十分之一都還沒有弄懂,但是一個下午的研究也不是毫無收穫。這篇部落格就來簡單講講我對IoC
的理解,以及Spring
中,IoC
最基本的實現流程。
二、正文
2.1 什麼是IoC
在我們使用傳統的編碼方式編寫程式碼時,如果在類中需要引用另外一個類的物件,我們一般會直接在類中使用new
關鍵字,建立這樣一個物件。此時,使用這個物件的類,就與這個物件所對應的類產生了耦合性。
IoC
中文名稱為控制反轉,它就是用來解決上述問題的一種設計思想,它能指導我們設計出耦合性更低,更易於管理的程式碼。傳統的程式,都是在需要使用的地方主動地建立物件,而IoC
的思想卻是將建立物件的工作交給IoC
容器去進行。我們告訴IoC
容器,它需要建立那些物件,IoC
容器建立好這些物件,然後主動地將它們注入到需要使用的地方。此時,需要使用物件的那些類,並不主動地獲取物件,而且由IoC
容器為它們分配,控制權在IoC
容器手上,這就是控制反轉。當然,IoC
容器並不只是控制物件,可以理解為控制外部資源的獲取。
IoC
很好的體現了物件導向設計法則之一—— 好萊塢法則:“別找我們,我們找你”;即由IoC
容器幫物件找相應的依賴物件並注入,而不是由物件主動去找。
2.2 IoC和DI的關係
DI
中文名稱為依賴注入,它其實和IoC
是相同的概念,或者可以理解為它是IoC
的一種具體的實現方式。IoC
的概念可能比較模糊,控制反轉只是一種思想,可能僅僅只是停留在由其他元件控制物件,而不是在使用的地方直接建立這一層面,但是具體如何實現並沒有指明。而DI
就是它的一種實現思路,由容器建立物件,並主動將物件注入到需要使用它的地方。2.1
中對IoC
的解釋,實際上更加偏向於DI
。
2.3 Spring如何實現IoC
這一塊,我就簡單地說一說我今天下午在研究Spring
的IoC
原始碼的過程中,瞭解到的一些內容。首先,Spring
的IoC
容器,可以簡單地理解為就是BeanFactory
介面的一個實現類物件,比如Spring
的應用上下文介面ApplicationContext
就是繼承自BeanFactory
,而我們使用較多的ClassPathXmlApplicationContext
就是ApplicationContext
的一個實現類。它們都可以理解為是Spring
的IoC
容器,而BeanFactory
就是這個繼承體系中的最高層。下面我就以單例bean的建立,簡單地說一說Spring
的IoC
實現的一個過程,假設使用到的是ClassPathXmlApplicationContext
這個容器,解析的是xml
配置檔案:
- 容器解析
xml
配置檔案,將宣告在xml
檔案中的bean
的配置資訊提取出來,每一個bean
的配置資訊被封裝成一個BeanDefinitionHolder
物件。BeanDefinitionHolder
主要包含三個成員——BeanDefinition
物件,bean
的名稱(id
),以及bean
的別名。BeanDefinition
物件就是用來儲存一個bean
的配置資訊,比如bean
的作用域,bean
的型別,bean
的依賴,bean
的屬性...... - 容器建立一個
ConcurrentHashMap
物件,這個map
的key
是bean
的名稱,而value
則是BeanDefinition
物件的引用。容器將第一步中解析出的每一個BeanDefinitionHolder
物件,它對應的bean
名稱,以及擁有的BeanDefinition
引用放入這個map
中。所以,這個map
儲存了我們在程式中宣告的所有的bean
的配置資訊。 - 容器初始化完成後,開始建立
bean
,遍歷第二步中的map
集合,獲取到bean
的名稱以及對應的BeanDefinition
物件,通過BeanDefinition
物件中的資訊,判斷這個bean
有沒有定義為延遲載入,若沒有,則需要現在就進行建立。在建立前,先通過BeanDefinition
中的配置資訊,判斷此bean
有沒有depend-on
(依賴)其他bean
,若有,則先建立當前bean
依賴的bean
(此處的depend-on
不是bean
的屬性,而是需要通過配置項進行配置的)。之後,則建立當前遍歷到的bean
。 bean
在建立完成後,通過bean
的BeanDefinition
物件,獲取bean
需要注入值的屬性,然後為屬性賦值,若屬性的值型別是其他的bean
,則以上面相同的步驟,建立屬性對應的bean
;- 容器建立一個
ConcurrentHashMap
,將建立好的單例bean
儲存在其中。我們在程式碼中可以通過容器的getBean
方法,傳入bean
的名稱或型別獲取單例bean
。容器會先去這個map
中查詢,若map
中不存在,且這個bean
的配置在第2
步中儲存配置資訊的map
中能夠找到,則建立這個bean
,放入儲存bean
的map
中(因為bean
可以配置延遲載入,即第一次獲取時載入);
Spring
中,bean
最基本的兩種作用域就是singleton
(單例)和prototype
(多例),預設為單例。以上過程是單例bean
的建立過程,若作用域為prototype
,則每一次呼叫getBean
方法,都會建立一個新的bean
。順帶一提,建立bean
的方式是通過反射機制,這個大部分人應該都知道。
三、總結
以上內容是我根據自己的認識,對Spring
的IoC
做的一次簡單記錄,內容並不全面,因為我目前對它的理解也比較淺顯。在今天閱讀Spring
原始碼的過程中,我發現它真的比我想象中要複雜很多,或許是我水平有限,又或許是沒有掌握閱讀原始碼的方法,讀起來真的非常吃力。總而言之,想要真正讀懂Spring
,我還需要很多的學習,希望今後能夠儘快提升自己,早日將Spring
吃透。