實驗樓-Spring框架基礎入門-Spring IoC容器
實驗樓-Spring框架基礎入門-Spring IoC容器
目錄
Spring Inner Bean - 內部巢狀的 Bean
Spring Bean Scopes - Bean 的作用域
Spring Collections - 集合型別的 Bean
使用者在使用 Spring 所提供的各種功能之前,必須在 Spring IoC 容器中裝配好 Bean,並建立 Bean 和 Bean 之間的關聯關係。本節將帶你學習 Spring 的 IoC 容器。
知識點
- IoC 容器
- Bean 屬性注入 value
- 內部巢狀的 Bean
- 集合型別的 Bean
- Bean 的作用域
- Spring 註解的配置
- Spring Bean 的生命週期
理論基礎
IoC 是什麼
Ioc,Inversion of Control,即“控制反轉”。它不是什麼技術,而是一種設計思想。在 Java 開發中,Ioc 意味著將你設計好的物件交給容器控制,而不是傳統的在你的物件內部直接控制。如何理解好 Ioc 呢?理解好 Ioc 的關鍵是要明確“誰控制誰,控制什麼,為何是反轉(有反轉就應該有正轉了),哪些方面反轉了”,那我們來深入分析一下:
-
誰控制誰,控制什麼:傳統 Java SE 程式設計,我們直接在物件內部通過 new 進行建立物件,是程式主動去建立依賴物件;而 IoC 是有專門一個容器來建立這些物件,即由 IoC 容器來控制物件的建立;誰控制誰?當然是 IoC 容器控制了物件;控制什麼?那就是主要控制了外部資源獲取(不只是物件,還包括檔案等)。
-
為何是反轉,哪些方面反轉了:有反轉就有正轉,傳統應用程式是由我們自己在物件中主動控制去直接獲取依賴物件,也就是正轉;而反轉則是由容器來幫忙建立及注入依賴物件;為何是反轉?因為由容器幫我們查詢及注入依賴物件,物件只是被動的接受依賴物件,所以是反轉;哪些方面反轉了?依賴物件的獲取被反轉了。
用圖例說明一下,傳統程式設計都是主動去建立相關物件然後再組合起來:
當有了 IoC/DI 的容器後,在客戶端類中不再主動去建立這些物件了,如圖:
IoC 能做什麼
IoC 不是一種技術,只是一種思想,一個重要的面向物件程式設計的法則,它能指導我們如何設計出鬆耦合、更優良的程式。傳統應用程式都是由我們在類內部主動建立依賴物件,從而導致類與類之間高耦合,難於測試;有了 IoC 容器後,把建立和查詢依賴物件的控制權交給了容器,由容器進行注入組合物件,所以物件與物件之間是鬆散耦合,這樣也方便測試,利於功能複用,更重要的是使得程式的整個體系結構變得非常靈活。
其實 IoC 對程式設計帶來的最大改變不是從程式碼上,而是從思想上,發生了“主從換位”的變化。應用程式原本是老大,要獲取什麼資源都是主動出擊,但是在 IoC/DI 思想中,應用程式就變成被動的了,被動的等待 IoC 容器來建立並注入它所需要的資源了。
IoC 很好的體現了面向物件設計法則之一的好萊塢法則:“別找我們,我們找你”。即由 IoC 容器幫物件找相應的依賴物件並注入,而不是由物件主動去找。
IoC 和 DI
DI,Dependency Injection,即“依賴注入”:是元件之間依賴關係由容器在執行期決定,形象的說,即由容器動態的將某個依賴關係注入到元件之中。依賴注入的目的並非為軟體系統帶來更多功能,而是為了提升元件重用的頻率,併為系統搭建一個靈活、可擴充套件的平臺。通過依賴注入機制,我們只需要通過簡單的配置,而無需任何程式碼就可指定目標需要的資源,完成自身的業務邏輯,而不需要關心具體的資源來自何處,由誰實現。
理解 DI 的關鍵是:“誰依賴誰,為什麼需要依賴,誰注入誰,注入了什麼”。我們來深入分析一下:
- 誰依賴於誰:當然是某個容器管理物件依賴於 IoC 容器;“被注入物件的物件”依賴於“依賴物件”。
- 為什麼需要依賴:容器管理物件需要 IoC 容器來提供物件需要的外部資源。
- 誰注入誰:很明顯是 IoC 容器注入某個物件,也就是注入“依賴物件”。
- 注入了什麼:就是注入某個物件所需要的外部資源,包括物件、資源、常量資料。
IoC 和 DI 有什麼關係呢?其實它們是同一個概念的不同角度描述,由於控制反轉概念比較含糊(可能只是理解為容器控制物件這一個層面,很難讓人想到誰來維護物件關係),所以 2004 年大師級人物 Martin Fowler 又給出了一個新的名字:“依賴注入”,相對 IoC 而言,“依賴注入”明確描述了“被注入物件依賴 IoC 容器配置依賴物件”。
IoC 容器
IoC 容器就是具有依賴注入功能的容器,IoC 容器負責例項化、定位、配置應用程式中的物件及建立這些物件間的依賴。應用程式無需直接在程式碼中 new 相關的物件,應用程式由 IoC 容器進行組裝。在 Spring 中 BeanFactory 是 IoC 容器的實際代表者。
Spring IoC 容器如何知道哪些是它管理的物件呢?
這就需要配置檔案,Spring IoC 容器通過讀取配置檔案中的配置元資料,通過元資料對應用中的各個物件進行例項化及裝配。一般使用基於 xml 配置檔案進行配置元資料,而且 Spring 與配置檔案完全解耦的,可以使用其他任何可能的方式進行配置元資料,比如註解、基於 java 檔案的、基於屬性檔案的配置都可以。
在 Spring Ioc 容器的代表就是 org.springframework.beans 包中的 BeanFactory 介面,BeanFactory 介面提供了 IoC 容器最基本功能;而 org.springframework.context 包下的 ApplicationContext 介面擴充套件了 BeanFactory,還提供了與 Spring AOP 整合、國際化處理、事件傳播及提供不同層次的 context 實現,如針對 web 應用的 WebApplicationContext。簡單說,BeanFactory 提供了 IoC 容器最基本功能,而 ApplicationContext 則增加了更多支援企業級功能支援。ApplicationContext 完全繼承 BeanFactory,因而 BeanFactory 所具有的語義也適用於 ApplicationContext。
- XmlBeanFactory:BeanFactory 實現,提供基本的 IoC 容器功能,可以從 classpat h 或檔案系統等獲取資源。
-
ClassPathXmlApplicationContext:ApplicationContext 實現,從 classpath 獲取配置檔案。
-
FileSystemXmlApplicationContext:ApplicationContext 實現,從檔案系統獲取配置檔案。
Spring 中 Bean 的定義及注入 Value
Spring 中,bean 的定義有三種方式:
- 基於 XML 的配置
- 基於註解的配置
- 基於 Java 類的配置
Bean 的注入有兩種方式:基於建構函式的依賴注入和基於設值函式的依賴注入。
Spring Inner Bean - 內部巢狀的 Bean
內部巢狀的 Bean 支援屬性(property)注入和建構函式(constructor - arg)注入。
Spring Bean Scopes - Bean 的作用域
在 Spring 中,Bean 的作用域決定了從 Spring 容器中返回的 Bean 例項的型別。在 Spring 中,支援以下 5 種類型的作用域:
- singleton — 單例模式,由 IOC 容器返回一個唯一的 bean 例項。
- prototype — 原型模式,被請求時,每次返回一個新的 bean 例項。
- request — 每個 HTTP Request 請求返回一個唯一的 Bean 例項。
- session — 每個 HTTP Session 返回一個唯一的 Bean 例項。
- globalSession — Http Session 全域性 Bean 例項。
注:大多數情況下,你可能只需要處理 Spring 的核心作用域 — 單例模式(singleton)和原型模式(prototype),預設情況下,作用域是單例模式。
Spring Collections - 集合型別的 Bean
下面講怎樣將值注入集合型別,包含以下四種主要的集合型別:
- List
- Set
- Map
- Properties
Spring Bean 的生命週期
Spring 框架中,一旦把一個 Bean 納入 Spring IOC 容器之中,這個 Bean 的生命週期就會交由容器進行管理,一般擔當管理角色的是 BeanFactory 或者 ApplicationContext,認識一下 Bean 的生命週期活動,對更好的利用它有很大的幫助:
下面以 BeanFactory 為例,說明一個 Bean 的生命週期活動
- Bean 的建立,由 BeanFactory 讀取 Bean 定義檔案,並生成各個例項。
- Setter 注入,執行 Bean 的屬性依賴注入。
- BeanNameAware 的
setBeanName()
,如果實現該介面,則執行其 setBeanName 方法 - BeanFactoryAware 的
setBeanFactory()
,如果實現該介面,則執行其 setBeanFactory 方法。 - BeanPostProcessor 的
processBeforeInitialization()
,如果有關聯的 processor,則在 Bean 初始化之前都會執行這個例項的processBeforeInitialization()
方法。 - InitializingBean 的
afterPropertiesSet()
,如果實現了該介面,則執行其afterPropertiesSet()
方法。 - Bean 定義檔案中定義 init-method。
- BeanPostProcessors 的
processAfterInitialization()
,如果有關聯的 processor,則在 Bean 初始化之前都會執行這個例項的processAfterInitialization()
方法。 - DisposableBean 的
destroy()
,在容器關閉時,如果 Bean 類實現了該介面,則執行它的destroy()
方法。 - Bean 定義檔案中定義 destroy-method,在容器關閉時,可以在 Bean 定義檔案中使用“destory-method”定義的方法。
如果使用 ApplicationContext 來維護一個 Bean 的生命週期,則基本上與上邊的流程相同,只不過在執行 BeanNameAware 的setBeanName()
後,若有 Bean 類實現了org.springframework.context.ApplicationContextAware
介面,則執行其setApplicationContext()
方法,然後再進行 BeanPostProcessors 的processBeforeInitialization()
。
實際上,ApplicationContext 除了向 BeanFactory 那樣維護容器外,還提供了更加豐富的框架功能,如 Bean 的訊息,事件處理機制等。