1. 程式人生 > 實用技巧 >實驗樓-Spring框架基礎入門-Spring IoC容器

實驗樓-Spring框架基礎入門-Spring IoC容器

實驗樓-Spring框架基礎入門-Spring IoC容器

目錄

實驗樓-Spring框架基礎入門-Spring IoC容器

理論基礎

IoC 是什麼

IoC 能做什麼

IoC 和 DI

IoC 容器

Spring 中 Bean 的定義及注入 Value

Spring Inner Bean - 內部巢狀的 Bean

Spring Bean Scopes - Bean 的作用域

Spring Collections - 集合型別的 Bean

Spring 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 的定義有三種方式:

  1. 基於 XML 的配置
  2. 基於註解的配置
  3. 基於 Java 類的配置

Bean 的注入有兩種方式:基於建構函式的依賴注入和基於設值函式的依賴注入。

Spring Inner Bean - 內部巢狀的 Bean

內部巢狀的 Bean 支援屬性(property)注入和建構函式(constructor - arg)注入。

Spring Bean Scopes - Bean 的作用域

在 Spring 中,Bean 的作用域決定了從 Spring 容器中返回的 Bean 例項的型別。在 Spring 中,支援以下 5 種類型的作用域:

  1. singleton — 單例模式,由 IOC 容器返回一個唯一的 bean 例項。
  2. prototype — 原型模式,被請求時,每次返回一個新的 bean 例項。
  3. request — 每個 HTTP Request 請求返回一個唯一的 Bean 例項。
  4. session — 每個 HTTP Session 返回一個唯一的 Bean 例項。
  5. 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 的訊息,事件處理機制等。