1. 程式人生 > 程式設計 >Spring IoC - Spring IoC 的設計

Spring IoC - Spring IoC 的設計

前言

本文為解讀Spring IoC 模組原始碼的開篇介紹。介紹Spring IoC 的相關概念與設計。

What is IoC

控制反轉(Inversion of Control,縮寫為IoC),是面向物件程式設計中的一種設計原則,可以用來減低計算機程式碼之間的耦合度。其中最常見的方式叫做依賴注入(Dependency Injection,簡稱DI),還有一種方式叫“依賴查詢”(Dependency Lookup)。通過控制反轉,物件在被建立的時候,由一個調控系統內所有物件的外界實體,將其所依賴的物件的引用傳遞(注入)給它。

-- 摘自維基百科

大型應用中,需要多個類組合工作來實現業務邏輯。這使得每個物件都需要在工作的時候獲取到與其合作的物件的引用。

如果這個獲取過程要靠自身來實現,那麼,程式碼會變得高度耦合並且難以測試。這對複雜的OOP系統的設計是非常不利的。

在OOP系統中,物件封裝了資料和對資料的處理動作,物件的依賴關係體現在了對資料和方法的依賴上。這些依賴關係,可以通過把物件的依賴注入交給框架或IoC容器來完成。

簡單來說:

  • 控制:當前物件對其內部成員物件的控制權/獲取組裝物件的過程
  • 反轉:上述的過程/控制權,交由專門的第三方元件(容器或者說平臺)來管理

這種從具體物件手中,交出控制的做法,在解耦程式碼的同時提高了程式碼的可測試性。好處具體如下:

  1. 不用自己組裝,拿來就用。
  2. 享受單例的好處,效率高,不浪費空間。
  3. 便於單元測試,方便切換mock元件。
  4. 便於進行AOP操作,對於使用者是透明的。
  5. 統一配置,便於修改。

Spring IoC

在Spring中,IoC容器是實現這個模式的載體,它可以在物件生成或初始化時直接將資料注入到物件中,也可以通過將物件引用注入到物件資料域中的方式來注入對方法呼叫的依賴。這種依賴注入是可以遞迴的,物件被逐層注入

就此而言,這種方案有一種完整而簡潔的美感,它把物件的依賴關係有序地建立起來,簡化了物件依賴關係的管理,在很大程度上簡化了面向物件系統的複雜性。

Spring IoC提供了一個基本的JavaBean容器,通過IoC模式管理依賴關係,並通過依賴注入和AOP切面增強了為JavaBean這樣的POJO物件賦予事務管理、生命週期管理等基本功能。

IoC 容器的設計

在Spring IOC 容器的設計當中,我們可以看到兩個主要的容器系列(根據命名),

  • 實現了BeanFactory介面的簡單容器系列,只實現了容器的最基本功能;
  • ApplicationContext應用上下文,容器的高階形態,增加了許多面向框架的特性和對應用環境的適配;

對於使用者來說,這些都是容器,是容器的不同表現形式,使用什麼樣的容器完全取決於使用者的需求。

undefined

BeanFactory

BeanFactory介面定義了IoC容器最基本的形式,並且提供了IoC容器所應該遵守的最基本的服務契約,同時,這也是我們使用IoC容器所應遵守的最底層和最基本的程式設計規範,這些介面定義勾畫出了IoC的基本輪廓。

往下的整個繼承樹是蠻複雜的,你也不需要所有都掌握,就想我們之前提過的怎麼學習原始碼,找核心類。現在來挑幾個重點的BeanFactory說一下。避免後面原始碼分析章節會一臉懵逼。

  • ListableBeanFactory,這個 Listable 的意思就是,通過這個介面,我們可以獲取多個 Bean,大家看原始碼會發現,最頂層 BeanFactory 介面的方法都是獲取單個 Bean 的。
  • ApplicationContext 繼承了 HierarchicalBeanFactory,Hierarchical 單詞本身已經能說明問題了,也就是說我們可以在應用中起多個 BeanFactory,然後可以將各個 BeanFactory 設定為父子關係。
  • AutowireCapableBeanFactory 這個名字中的 Autowire 大家都非常熟悉,它就是用來自動裝配 Bean 用的,但是仔細看上圖,ApplicationContext 並沒有繼承它,不過不用擔心,不使用繼承,不代表不可以使用組合,如果你看到 ApplicationContext 介面定義中的最後一個方法 getAutowireCapableBeanFactory() 就知道了。
  • DefaultListableBeanFactory,在Spring中,實際上是把DefaultListableBeanFactory作為一個預設的功能完整的IoC容器來使用的。包含了基本IoC容器所具有的重要功能。

以上介面,推薦大家去閱讀一下他們的JavaDoc,來瞭解作者是怎麼描述的。更為準確。

ApplicationContext

ApplicationContext是一個高階形態意義的IoC容器,ApplicationContext在BeanFactory的基礎上集成了MessageSource,ApplicationEventPublisher,ResourcePatternResolver這幾個介面,這些介面為ApplicationContext提供了以下BeanFactory不具備的新特性:

  • 支援不同的資訊源。我們看到ApplicationContext擴充套件了MessageSource介面,這些資訊源的擴充套件功能可以支援國際化的實現,為開發多語言版本的應用提供服務。
  • 訪問資源。這一特性體現在對ResourceLoader和Resource的支援上,這樣我們可以從不同地方得到Bean定義資源。這種抽象使使用者程式可以靈活地定義Bean定義資訊,尤其是從不同的I/O途徑得到Bean定義資訊。
  • 支援應用事件。繼承了介面ApplicationEventPublisher,從而在上下文中引入了事件機制。這些事件和Bean的生命週期的結合為Bean的管理提供了便利。
  • 在ApplicationContext中提供的附加服務。這些服務使得基本IoC容器的功能更豐富。因為具備了這些豐富的附加功能,使得ApplicationContext與簡單的BeanFactory相比,對它的使用是一種面向框架的使用風格,所以一般建議在開發應用時使用ApplicationContext作為IoC容器的基本形式。

BeanDefinition

在這些Spring提供的基本IoC容器的介面定義和實現的基礎上,Spring通過定義BeanDefinition來管理基於Spring的應用中的各種物件以及它們之間的相互依賴關係。

BeanDefinition 中儲存了我們的 Bean 資訊,比如這個 Bean 指向的是哪個類、是否是單例的、是否懶載入、這個 Bean 依賴了哪些 Bean 等等。

image.png

BeanDefinition抽象了我們對Bean的定義,是讓容器起作用的主要資料型別。對IoC容器來說,BeanDefinition就是對依賴反轉模式中管理的物件依賴關係的資料抽象,也是容器實現依賴反轉功能的核心資料結構,依賴反轉功能都是圍繞對這個BeanDefinition的處理來完成的。

這些BeanDefinition就像是容器裡裝的水,有了這些基本資料,容器才能夠發揮作用。

結語

接下去的章節我們會正式開始分析Spring IoC 的具體實現,容器的初始化和依賴注入是怎麼實現的。大部分都是原始碼解讀的內容。

如果之前沒有閱讀原始碼經驗的同學,可以先看看這篇你為什麼要看原始碼?如何看原始碼?

參考