1. 程式人生 > >Spring框架的設計理念與設計模式分析(一)

Spring框架的設計理念與設計模式分析(一)

Spring 的骨骼架構

  Spring 總共有十幾個元件,但是真正核心的元件只有幾個,下面是 Spring 框架的總體架構圖:

  圖 1 .Spring 框架的總體架構圖

Spring 框架的設計理念與設計模式分析

  從上圖中可以看出 Spring 框架中的核心元件只有三個:Core、Context 和 Beans。它們構建起了整個 Spring 的骨骼架構。沒有它們就不可能有 AOP、Web 等上層的特性功能。下面也將主要從這三個元件入手分析 Spring。

  Spring 的設計理念

  前面介紹了 Spring 的三個核心元件,如果再在它們三個中選出核心的話,那就非 Beans 元件莫屬了,為何這樣說,其實 Spring 就是面向 Bean 的程式設計(BOP,Bean Oriented Programming),Bean 在 Spring 中才是真正的主角。

  Bean 在 Spring 中作用就像 Object 對 OOP 的意義一樣,沒有物件的概念就像沒有面向物件程式設計,Spring 中沒有 Bean 也就沒有 Spring 存在的意義。就像一次演出舞臺都準備好了但是卻沒有演員一樣。為什麼要 Bean 這種角色 Bean 或者為何在 Spring 如此重要,這由 Spring 框架的設計目標決定,Spring 為何如此流行,我們用 Spring 的原因是什麼,想想你會發現原來 Spring 解決了一個非常關鍵的問題他可以讓你把物件之間的依賴關係轉而用配置檔案來管理,也就是他的依賴注入機制。而這個注入關係在一個叫 Ioc 容器中管理,那 Ioc 容器中有又是什麼就是被 Bean 包裹的物件。Spring 正是通過把物件包裝在 Bean 中而達到對這些物件管理以及一些列額外操作的目的。

  它這種設計策略完全類似於 Java 實現 OOP 的設計理念,當然了 Java 本身的設計要比 Spring 複雜太多太多,但是都是構建一個數據結構,然後根據這個資料結構設計他的生存環境,並讓它在這個環境中按照一定的規律在不停的運動,在它們的不停運動中設計一系列與環境或者與其他個體完成資訊交換。這樣想來回過頭想想我們用到的其他框架都是大概類似的設計理念。

  核心元件如何協同工作

  前面說 Bean 是 Spring 中關鍵因素,那 Context 和 Core 又有何作用呢?前面把 Bean 比作一場演出中的演員的話,那 Context 就是這場演出的舞臺背景,而 Core 應該就是演出的道具了。只有他們在一起才能具備能演出一場好戲的最基本的條件。當然有最基本的條件還不能使這場演出脫穎而出,還要他表演的節目足夠的精彩,這些節目就是 Spring 能提供的特色功能了。

  我們知道 Bean 包裝的是 Object,而 Object 必然有資料,如何給這些資料提供生存環境就是 Context 要解決的問題,對 Context 來說他就是要發現每個 Bean 之間的關係,為它們建立這種關係並且要維護好這種關係。所以 Context 就是一個 Bean 關係的集合,這個關係集合又叫 Ioc 容器,一旦建立起這個 Ioc 容器後, Spring 就可以為你工作了。那 Core 元件又有什麼用武之地呢?其實 Core 就是發現、建立和維護每個 Bean 之間的關係所需要的一些列的工具,從這個角度看來,Core 這個元件叫 Util 更能讓你理解。

  它們之間可以用下圖來表示:

  圖 2. 三個元件關係

2


  核心元件詳解

  這裡將詳細介紹每個元件內部類的層次關係,以及它們在執行時的時序順序。我們在使用 Spring 是應該注意的地方。

  Bean 元件

  前面已經說明了 Bean 元件對 Spring 的重要性,下面看看 Bean 這個元件式怎麼設計的。Bean 元件在 Spring 的 org.springframework.beans 包下。這個包下的所有類主要解決了三件事:Bean 的定義、Bean 的建立以及對 Bean 的解析。對 Spring 的使用者來說唯一需要關心的就是 Bean 的建立,其他兩個由 Spring 在內部幫你完成了,對你來說是透明的。

  Spring Bean 的建立時典型的工廠模式,他的頂級介面是 BeanFactory,下圖是這個工廠的繼承層次關係:

  圖 4. Bean 工廠的繼承關係

核心元件:

  BeanFactory 有三個子類:ListableBeanFactory、HierarchicalBeanFactory 和 AutowireCapableBeanFactory。但是從上圖中我們可以發現最終的預設實現類是 DefaultListableBeanFactory,他實現了所有的介面。那為何要定義這麼多層次的介面呢?查閱這些介面的原始碼和說明發現,每個介面都有他使用的場合,它主要是為了區分在 Spring 內部在操作過程中物件的傳遞和轉化過程中,對物件的資料訪問所做的限制。例如 ListableBeanFactory 介面表示這些 Bean 是可列表的,而 HierarchicalBeanFactory 表示的是這些 Bean 是有繼承關係的,也就是每個 Bean 有可能有父 Bean。AutowireCapableBeanFactory 介面定義 Bean 的自動裝配規則。這四個介面共同定義了 Bean 的集合、Bean 之間的關係、以及 Bean 行為。

        Bean 的定義主要有 BeanDefinition 描述,如下圖說明了這些類的層次關係:

  圖 5. Bean 定義的類層次關係圖

核心元件:

  Bean 的定義就是完整的描述了在 Spring 的配置檔案中你定義的節點中所有的資訊,包括各種子節點。當Spring 成功解析你定義的一個節點後,在 Spring 的內部他就被轉化成 BeanDefinition 物件。以後所有的操作都是對這個物件完成的。

  Bean 的解析過程非常複雜,功能被分的很細,因為這裡需要被擴充套件的地方很多,必須保證有足夠的靈活性,以應對可能的變化。Bean 的解析主要就是對 Spring 配置檔案的解析。這個解析過程主要通過下圖中的類完成:

  圖 6. Bean 的解析類

核心元件:

  當然還有具體對 tag 的解析這裡並沒有列出。

  Context 元件

  Context 在 Spring 的 org.springframework.context 包下,前面已經講解了 Context 元件在 Spring 中的作用,他實際上就是給 Spring 提供一個執行時的環境,用以儲存各個物件的狀態。下面看一下這個環境是如何構建的。

  ApplicationContext 是 Context 的頂級父類,他除了能標識一個應用環境的基本資訊外,他還繼承了五個介面,這五個介面主要是擴充套件了 Context 的功能。下面是 Context 的類結構圖:

  圖 7. Context 相關的類結構圖

核心元件:Context 元件

  從上圖中可以看出 ApplicationContext 繼承了 BeanFactory,這也說明了 Spring 容器中執行的主體物件是 Bean,另外 ApplicationContext 繼承了 ResourceLoader 介面,使得 ApplicationContext 可以訪問到任何外部資源,這將在 Core 中詳細說明。

  ApplicationContext 的子類主要包含兩個方面:

  ConfigurableApplicationContext 表示該 Context 是可修改的,也就是在構建 Context 中使用者可以動態新增或修改已有的配置資訊,它下面又有多個子類,其中最經常使用的是可更新的 Context,即 AbstractRefreshableApplicationContext 類。

  WebApplicationContext 顧名思義,就是為 web 準備的 Context 他可以直接訪問到 ServletContext,通常情況下,這個介面使用的少。

  再往下分就是按照構建 Context 的檔案型別,接著就是訪問 Context 的方式。這樣一級一級構成了完整的 Context 等級層次。

  總體來說 ApplicationContext 必須要完成以下幾件事:

  標識一個應用環境

  利用 BeanFactory 建立 Bean 物件

  儲存物件關係表

  能夠捕獲各種事件

  Context 作為 Spring 的 Ioc 容器,基本上整合了 Spring 的大部分功能,或者說是大部分功能的基礎。

  Core 元件

  Core 元件作為 Spring 的核心元件,他其中包含了很多的關鍵類,其中一個重要組成部分就是定義了資源的訪問方式。這種把所有資源都抽象成一個介面的方式很值得在以後的設計中拿來學習。下面就重要看一下這個部分在 Spring 的作用。

  下圖是 Resource 相關的類結構圖:

  圖 8. Resource 相關的類結構圖

核心元件:Core 元件

  從上圖可以看出 Resource 介面封裝了各種可能的資源型別,也就是對使用者來說遮蔽了檔案型別的不同。對資源的提供者來說,如何把資源包裝起來交給其他人用這也是一個問題,我們看到 Resource 介面繼承了 InputStreamSource 介面,這個介面中有個 getInputStream 方法,返回的是 InputStream 類。這樣所有的資源都被可以通過 InputStream 這個類來獲取,所以也遮蔽了資源的提供者。另外還有一個問題就是載入資源的問題,也就是資源的載入者要統一,從上圖中可以看出這個任務是由 ResourceLoader 介面完成,他遮蔽了所有的資源載入者的差異,只需要實現這個介面就可以載入所有的資源,他的預設實現是 DefaultResourceLoader。