它這種設計政策完全類似于Java實作OOP的設計理念,當然Java本身的設計要比Spring複雜太多太多,但是它們都是建構一個資料結構,然後根據這個資料結構設計它的生存環境,并讓它在這個環境中按照一定的規律不停地運動,在它們的不停運動中設計一系列與環境或者與其他個體完成資訊交換。這樣想來我們用到的其他架構都是大概類似的設計理念。
Spring總共有十幾個元件,但是真正核心的元件隻有幾個。從圖中可以看出,Spring架構中的核心元件隻有三個:Core、Context和Bean。它們建構起了整個Spring的骨骼架構,沒有它們就不可能有AOP、Web等上層的特性功能。如果要在它們三個中選出核心,那就非Bean元件莫屬了。其實Spring就是面向Bean的程式設計(BOP,Bean Oriented Programming),Bean在Spring中才是真正的主角。

可以把Bean比做一場演出中的演員,Context就是這場演出的舞台背景,而Core應該就是演出的道具了。隻有它們在一起才能具備能演出一場好戲的最基本的條件。當然有最基本的條件還不能使這場演出脫穎而出,還要它表演的節目足夠精彩,這些節目就是Spring能提供的特色功能了。
Spring解決了一個非常關鍵的問題,它可以讓你把對象之間的依賴關系轉而用配置檔案來管理,也就是它的依賴注入機制。而這個注入關系在一個叫Ioc的容器中管理,Bean包裝的是Object,而Object必然有資料,如何給這些資料提供生存環境就是Context要解決的問題,對Context來說它就是要發現每個Bean之間的關系,為它們建立這種關系并且維護好這種關系。是以Context就是一個Bean關系的集合,這個關系集合就叫Ioc容器。一旦建立起這個Ioc容器Spring就可以為你工作了。其實Core就是發現、建立和維護每個Bean之間的關系所需要的一系列工具,從這個角度來看,Core元件叫Util更能讓你了解。
Bean元件在Spring的org.springframework.beans包下。這個包下的所有類主要解決了三件事:Bean的定義、Bean的建立及對Bean的解析。對Spring的使用者來說唯一需要關心的就是Bean的建立,其他兩個由Spring在内部幫你完成了,對你來說是透明的。
Spring Bean的建立是典型的工廠模式,它的頂級接口是BeanFactory。
BeanFactory有三個子類:ListableBeanFactory、HierarchicalBeanFactory和AutowireCapableBeanFactory。但是從圖中我們可以發現最終的預設實作類是DefaultListableBeanFactory,它實作了所有的接口。查閱這些接口的源碼和說明可以發現每個接口都有它使用的場合,它主要是為了區分在Spring内部對象的傳遞和轉化過程中,對對象的資料通路所做的限制。例如,ListableBeanFactory接口表示這些Bean是可清單的,而HierarchicalBeanFactory表示的是這些Bean是有繼承關系的,也就是每個Bean有可能有父Bean,AutowireCapableBeanFactory接口定義Bean的自動裝配規則。這四個接口共同定義了Bean的集合、Bean之間的關系和Bean的行為。
Bean的定義主要由BeanDefinition描述。
Bean的定義完整地描述了在Spring的配置檔案中你定義的節點中所有的資訊,包括各種子節點。當Spring成功解析你定義的一個節點後,在Spring的内部它就被轉化成BeanDefinition對象,以後所有的操作都是對這個對象進行的。Bean的解析過程非常複雜,功能被分得很細,因為這裡需要被擴充的地方很多,必須保證有足夠的靈活性,以應對可能的變化。Bean的解析主要就是對Spring配置檔案的解析。
Context在Spring的org.springframework.context包下,它實際上就是給Spring提供一個運作時的環境,用以儲存各個對象的狀态。ApplicationContext是Context的頂級父類,它除了能辨別一個應用環境的基本資訊外,還繼承了5個接口,這5個接口主要是擴充了Context的功能。可以看出 ApplicationContext 繼承了 BeanFactory,這也說明了 Spring 容器中運作的主體對象是 Bean,另外 ApplicationContext 繼承了 ResourceLoader 接口,使得 ApplicationContext 可以通路到任何外部資源。
ApplicationContext 的子類主要包含兩個方面:
ConfigurableApplicationContext 表示該 Context 是可修改的,也就是在建構 Context 中使用者可以動态添加或修改已有的配置資訊,它下面又有多個子類,其中最經常使用的是可更新的 Context,即 AbstractRefreshableApplicationContext 類。
WebApplicationContext 顧名思義,就是為 web 準備的 Context 他可以直接通路到 ServletContext,通常情況下,這個接口使用的少。
再往下分就是按照建構 Context 的檔案類型,接着就是通路 Context 的方式。這樣一級一級構成了完整的 Context 等級層次。
總體來說 ApplicationContext 必須要完成以下幾件事:
辨別一個應用環境
利用 BeanFactory 建立 Bean 對象
儲存對象關系表
能夠捕獲各種事件
Context 作為 Spring 的 Ioc 容器,基本上整合了 Spring 的大部分功能,或者說是大部分功能的基礎。
Core元件作為Spring的核心元件,其中包含了很多關鍵類,一個重要的組成部分就是定義了資源的通路方式。
可以看出Resource接口封裝了各種可能的資源類型,也就是對使用者來說屏蔽了檔案類型的不同。對資源的提供者來說,如何把資源包裝起來交給其他人用這也是一個問題,我們看到Resource接口繼承了InputStreamSource接口,這個接口中有個getInputStream方法,傳回的是InputStream類。這樣所有的資源都可以通過InputStream類來擷取,是以也屏蔽了資源的提供者。另外還有一個問題就是加載資源的問題,也就是資源的加載者要統一,從上圖中可以看出這個任務是由ResourceLoader接口完成的,它屏蔽了所有的資源加載者的差異,隻需要實作這個接口就可以加載所有的資源,它的預設實作是DefaultResourceLoader。
Context和Resource是如何建立關系的?Context把資源的加載、解析和描述工作委托給了ResourcePatternResolver類來完成,它相當于一個接頭人,它把資源的加載、解析和資源的定義整合在一起便于其他元件使用。Core元件中還有很多類似的方式。
IOC作為一個容器,它裡面放得都是bean,bean與bean之間的對應關系,而bean之間的對應關系我們開始都是通過xml配置檔案來展現的。
那麼這裡就回報了如下幾個問題:
1、對應與對象之間的關系是通過xml配置檔案來描述的(當然也可以是properties等檔案)。
2、描述的檔案存放位置在那裡,一般來說我們都是放在classpath目錄下的,但是也可以是URL、fileSystem。
3、檔案的解析。
4、Bean在容器中的表現形式,也就是它的資料結構。
對于Spring而言,它用Resource、BeanDefinition、BeanDefinitionReader、BeanFactory、ApplicationContext五個元件來實作以上問題,而同時這5個接口定義了 spring ioc 容器的基本代碼元件結構。
下面我們簡單了解這五個結構
Resource
Resource,對資源的抽象,它的每一個實作類都代表了一種資源的通路政策,如ClasspathResource 、 URLResource ,FileSystemResource 等。
BeanDefinition
用來描述和抽象一個具體的Bean對象,它是描述Bean對象的基本資料結構。配置檔案中的每一個bean對應一個BeanDefinition執行個體。
BeanDefinitionReader
外部資源所表達的語義需要轉化為内部資料結構BeanDefinition,BeanDefinitionReader起到解析的作用。對應不同的描述需要有不同的Reader 。如 XmlBeanDefinitionReader 用來讀取xml 描述配置的 bean 對象。
BeanFactory
BeanFactory是一個純粹的bean容器,它是IOC必備的資料結構,其中BeanDefinition是它的基本結構,它内部維護着一個BeanDefinition map,并可根據BeanDefinition 的描述進行 bean 的建立和管理。
ApplicationContext
這個就是大名鼎鼎的Spring容器,它叫做應用上下文,與我們應用息息相關,它繼承BeanFactory,是BeanFactory的擴充更新版。由于ApplicationContext的結構就決定了它與BeanFactory的不同,其主要差別有:
繼承MessageSource,提供國際化的标準通路政策。
繼承ApplicationEventPublisher,提供強大的事件機制。
擴充ResourceLoader,可以用來加載多個Resource,可以靈活通路不同的資源。
對Web應用的支援。
Ioc容器實際上是Context元件結合其他兩個元件共同建構了一個Bean關系網,如何建構這個關系網?建構的入口就在AbstractApplicationContext類的refresh方法中,這個方法的代碼如下:
這個方法就是建構整個Ioc容器過程的完整的代碼,了解了裡面的每一行代碼基本上就了解大部分Spring的原理和功能。
這段代碼主要包含這樣幾個步驟:
建構BeanFactory,以便于産生所需的“演員”。
注冊可能感興趣的事件。
建立Bean執行個體對象。
觸發被監聽的事件。
第二三句就是在建立和配置 BeanFactory。這裡是 refresh 也就是重新整理配置,前面介紹了 Context 有可更新的子類,這裡正是實作這個功能,當 BeanFactory 已存在是就更新,如果沒有就新建立。下面是更新 BeanFactory 的方法代碼,這個方法實作了AbstractApplicationContext的抽象方法refreshBeanFactory,這段代碼清楚地說明了BeanFactory的建立過程:
注意BeanFactory對象的類型的變化,前面介紹了它有很多子類,在什麼情況下使用子類非常關鍵。BeanFactory的原始對象是DefaultListableBeanFactory,這非常關鍵,因為它涉及後面對這個對象的多種操作,下面看一下這個類的繼承關系圖。
建立 BeanFactory 時序圖
Bean 的解析和登記流程時序圖如下:
建立好BeanFactory後,添加一些Spring本身需要的一些工具類,這個操作在AbstractApplicationContext的prepareBeanFactory方法中完成。
在invokeBeanFactoryPostProcessors方法中主要是擷取實作BeanFactoryPostProcessor接口的子類,并執行它的postProcessBeanFactory方法,這個方法的聲明如下:
它的參數是beanFactory,說明可以對beanFactory做修改,這裡注意,beanFactory是ConfigurableListableBeanFactory類型的,這也印證了前面介紹的不同BeanFactory所使用的場合不同,這裡隻能是可配置的BeanFactory,防止一些資料被使用者随意修改。
在registerBeanPostProcessors方法中可以擷取使用者定義的實作了BeanPostProcessor接口的子類,并把它們注冊到BeanFactory對象中的beanPostProcessors變量中。BeanPostProcessor中聲明了兩個方法:postProcessBeforeInitialization和postProcessAfterInitialization,分别用于在Bean對象初始化時執行,可以執行使用者自定義的操作。
後面的幾行代碼是初始化監聽事件和對系統的其他監聽者的注冊,監聽者必須是ApplicationListener的子類。
下面就是Bean的執行個體化代碼,是從finishBeanFactoryInitialization方法開始的。
從上面的代碼中可以發現Bean的執行個體化是在BeanFactory中發生的。PreInstantiateSingletons方法的代碼如下:
這裡出現了一個非常重要的Bean——FactoryBean,可以說Spring一大半的擴充功能都與這個Bean有關,這是個特殊的Bean。它是個工廠Bean,可以産生Bean的Bean,這裡的産生Bean是指Bean的執行個體,如果一個類繼承FactoryBean使用者可以自己定義産生執行個體對象的方法,隻需實作它的getObject方法。然而在Spring内部,這個Bean的執行個體對象是FactoryBean,通過調用這個對象的getObject方法就能擷取使用者自定義産生的對象,進而為Spring提供了很好的擴充性。Spring擷取FactoryBean本身的對象是通過在前面加上&來完成的。
如何建立Bean的執行個體對象及如何建構Bean執行個體對象之間的關聯關系是Spring中的一個核心,這個過程的流程圖如下:
如果是普通的 Bean 就直接建立他的執行個體,是通過調用 getBean 方法。下面是建立 Bean 執行個體的時序圖:
還有一個非常重要的部分就是建立 Bean 對象執行個體之間的關系,這也是 Spring 架構的核心競争力,何時、如何建立他們之間的關系請看下面的時序圖:
如何讓這些 Bean 對象有一定的擴充性,就是可以加入使用者的一些操作。對 Spring 的 Ioc 容器來說,主要有以下幾個。
BeanFactoryPostProcessor, BeanPostProcessor。他們分别是在建構 BeanFactory 和建構 Bean 對象時調用。
InitializingBean 和 DisposableBean 分别是在 Bean 執行個體建立和銷毀時被調用。使用者可以實作這些接口中定義的方法,Spring會在适當的時候調用。
FactoryBean 是個特殊的 Bean,如果一個類繼承FactoryBean,使用者可以自己定義産生執行個體對象的方法,隻需實作它的getObject方法。
這些擴充點通常也是我們使用 Spring 來完成我們特定任務的地方,如何精通 Spring 就看你有沒有掌握好 Spring 有哪些擴充點,并且如何使用他們,要知道如何使用他們就必須了解他們内在的機理。可以用下面一個比喻來解釋。
我們把 Ioc 容器比作一個箱子,這個箱子裡有若幹個球的模子,可以用這些模子來造很多種不同的球,還有一個造這些球模的機器,這個機器可以産生球模。那麼他們的對應關系就是 BeanFactory 就是那個造球模的機器,球模就是 Bean,而球模造出來的球就是 Bean 的執行個體。
所說的幾個擴充點又在什麼地方呢?BeanFactoryPostProcessor 對應到當造球模被造出來時,你将有機會可以對其做出設當的修正,也就是他可以幫你修改球模。而 InitializingBean 和 DisposableBean 是在球模造球的開始和結束階段,你可以完成一些預備和掃尾工作。BeanPostProcessor 就可以讓你對球模造出來的球後做出适當的修正。最後還有一個 FactoryBean,它可是一個神奇的球模。這個球模不是預先就定型了,而是由你來給他确定它的形狀,既然你可以确定這個球模型的形狀,當然他造出來的球肯定就是你想要的球了,這樣在這個箱子裡尼可以發現所有你想要的球。
我們使用 Spring 必須要首先建構 Ioc 容器,沒有它 Spring 無法工作,ApplicatonContext.xml 就是 Ioc 容器的預設配置檔案,Spring 的所有特性功能都是基于這個 Ioc 容器工作的,比如AOP,JDBC。
Ioc 它實際上就是為你建構了一個魔方,Spring 為你搭好了骨骼架構,這個魔方到底能變出什麼好的東西出來,這必須要有你的參與。那我們怎麼參與?這就是前面說的要了解 Spring 中那有些擴充點,我們通過實作那些擴充點來改變 Spring 的通用行為。至于如何實作擴充點來得到我們想要的個性結果,Spring 中有很多例子,其中 AOP 的實作就是 Spring 本身實作了其擴充點來達到了它想要的特性功能,可以拿來參考。