簡單談談Spring的IoC
阿新 • • 發佈:2020-05-10
# 一、前言
這幾天正在複習``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``配置檔案: 1. 容器解析``xml``配置檔案,將宣告在``xml``檔案中的``bean``的配置資訊提取出來,每一個``bean``的配置資訊被封裝成一個``BeanDefinitionHolder``物件。``BeanDefinitionHolder``主要包含三個成員——``BeanDefinition``物件,``bean``的名稱(``id``),以及``bean``的別名。``BeanDefinition``物件就是用來儲存一個``bean``的配置資訊,比如``bean``的作用域,``bean``的型別,``bean``的依賴,``bean``的屬性...... 2. 容器建立一個``ConcurrentHashMap``物件,這個``map``的``key``是``bean``的名稱,而``value``則是``BeanDefinition``物件的引用。容器將第一步中解析出的每一個``BeanDefinitionHolder``物件,它對應的``bean``名稱,以及擁有的``BeanDefinition``引用放入這個``map``中。所以,這個``map``儲存了我們在程式中宣告的所有的``bean``的配置資訊。 3. 容器初始化完成後,開始建立``bean``,遍歷第二步中的``map``集合,獲取到``bean``的名稱以及對應的``BeanDefinition``物件,通過``BeanDefinition``物件中的資訊,判斷這個``bean``有沒有定義為延遲載入,若沒有,則需要現在就進行建立。在建立前,先通過``BeanDefinition``中的配置資訊,判斷此``bean``有沒有``depend-on``(依賴)其他``bean``,若有,則先建立當前``bean``依賴的``bean``(此處的``depend-on``不是``bean``的屬性,而是需要通過配置項進行配置的)。之後,則建立當前遍歷到的``bean``。 4. ``bean``在建立完成後,通過``bean``的``BeanDefinition``物件,獲取``bean``需要注入值的屬性,然後為屬性賦值,若屬性的值型別是其他的``bean``,則以上面相同的步驟,建立屬性對應的``bean``; 5. 容器建立一個``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``吃透。
# 四、參考 -
-
# 二、正文 ## 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``配置檔案: 1. 容器解析``xml``配置檔案,將宣告在``xml``檔案中的``bean``的配置資訊提取出來,每一個``bean``的配置資訊被封裝成一個``BeanDefinitionHolder``物件。``BeanDefinitionHolder``主要包含三個成員——``BeanDefinition``物件,``bean``的名稱(``id``),以及``bean``的別名。``BeanDefinition``物件就是用來儲存一個``bean``的配置資訊,比如``bean``的作用域,``bean``的型別,``bean``的依賴,``bean``的屬性...... 2. 容器建立一個``ConcurrentHashMap``物件,這個``map``的``key``是``bean``的名稱,而``value``則是``BeanDefinition``物件的引用。容器將第一步中解析出的每一個``BeanDefinitionHolder``物件,它對應的``bean``名稱,以及擁有的``BeanDefinition``引用放入這個``map``中。所以,這個``map``儲存了我們在程式中宣告的所有的``bean``的配置資訊。 3. 容器初始化完成後,開始建立``bean``,遍歷第二步中的``map``集合,獲取到``bean``的名稱以及對應的``BeanDefinition``物件,通過``BeanDefinition``物件中的資訊,判斷這個``bean``有沒有定義為延遲載入,若沒有,則需要現在就進行建立。在建立前,先通過``BeanDefinition``中的配置資訊,判斷此``bean``有沒有``depend-on``(依賴)其他``bean``,若有,則先建立當前``bean``依賴的``bean``(此處的``depend-on``不是``bean``的屬性,而是需要通過配置項進行配置的)。之後,則建立當前遍歷到的``bean``。 4. ``bean``在建立完成後,通過``bean``的``BeanDefinition``物件,獲取``bean``需要注入值的屬性,然後為屬性賦值,若屬性的值型別是其他的``bean``,則以上面相同的步驟,建立屬性對應的``bean``; 5. 容器建立一個``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``吃透。
# 四、參考 -