Spring技術內幕(2)Spring FrameWork的核心:IoC容器的實現

本章內容:

Spring IoC容器概述、IoC容器系列的設計與實現:BeanFactory和ApplicationContext、IoC容器初始化過程、IoC容器的依賴注入、容器其他相關特性的設計與實現。

2.1 Spring IoC容器概述

2.1.1 IoC容器和依賴反轉模式

在面向對象中,對象封裝了數據和對數據處理,對象的依賴關係體現在對數據和方法的依賴上。這些依賴關係可以通過把對象的依賴注入交給框架或IoC容器來完成,這樣做可以解耦代碼也可以提高代碼的可測試性。依賴控制反轉的實現方式很多,在Spring中IoC容器就是這個模式的載體(或實現),可它以在對象生成和初始化時直接將數據注入到對象中,也可以通過將對象引用注入到對象數據域中的方式來注入對方法調用的依賴,這種依賴注入是可以遞歸的,對象被逐層注入。控制反轉是關於一個對象如何獲取他所依賴對象的引用,在這裏是責任的反轉

IoC容器進行對象關係的管理,並由IoC容器完成對象的注入

2.1.2 Spring IoC的應用場景

在Spring中,SpringIoC提供了基本的JavaBean容器,通過IoC模式管理以來關係,並通過依賴注入和AOP切面增強了爲JavaBean這樣的POJO對象賦予事務管理、生命週期管理等基本功能。具體的注入方式有:接口注入、構造器注入、setter注入

 

2.2 IoC容器系列的設計與實現:BeanFactory和ApplicationContext

Spring IoC容器中有兩個主要的容器系列:(1)BeanFactory:實現了容器的基本功能;(2)ApplicationContext:在Beanfactory基礎上添加了功能,是容器的高級形式。

2.2.1 Spring IoC容器系列

Spring IoC容器系列概況:

BeanFactory和ApplicationContext都是IoC容器的具體表現形式。BeanFactory體現了Spring爲用戶使用IoC容器所設計的最基本的功能規範。在Spring提供的基本IoC容器接口定義和實現基礎上,Spring通過BeanDifinition來管理基於Spring的應用中的各種對象以及他們之間的相互依賴關係,BeanDefinition抽象了對Bean的定義,是容器起作用的主要數據類型。

2.2.2 Spring 的IoC容器的設計

  • 從BeanFactory到HierarchicalBeanFactory,再到ConfigurableBeanFactory,這是一條主要的Beanfactory設計路徑,該路徑定義了基本的IoC容器的規範。
  • 第二條接口設計主線是以ApplicationContext爲主的接口設計。
  • 這裏主要涉及的是接口關心,具體的IoC實現都是在這個接口體系下實現的,例如DefaultListableBeanFactory,是一個簡單的IoC容器的實現。像其他的IoC容器,例如XmlBeanFactory,都是在DefaultListableBeanFactory的基礎上擴展的,同樣ApplicationContext也是如此。

1、BeanFactory的應用場景

BeanFactory提供了最基本的IoC功能,這些可以在BeanFactory接口中看到。BeanFactory的具體實現類有:DefaultListableBeanFactory、XmlBeanFactory、ApplicationContext都是BeanFactory基礎上添加了相應的功能。

用戶使用容器時,可以使用轉義符“&”來獲得FactoryBean本身,用來區分獲取FactoryBean產生的對象和獲取FactoryBean本身。

 

BeanFactory與FactoryBean區別

BeanFactory是接口,提供了OC容器最基本的形式,給具體的IOC容器的實現提供了規範。
FactoryBean也是接口,爲IOC容器中Bean的實現提供了更加靈活的方式,FactoryBean在IOC容器的基礎上給Bean的實現加上了一個簡單工廠模式和裝飾模式(如果想了解裝飾模式參考:修飾者模式(裝飾者模式,Decoration) 我們可以在getObject()方法中靈活配置。其實在Spring源碼中有很多FactoryBean的實現類。
區別:BeanFactory是個Factory,也就是IOC容器或對象工廠,FactoryBean是個Bean。在Spring中,所有的Bean都是由BeanFactory(也就是IOC容器)來進行管理的。但對FactoryBean而言,這個Bean不是簡單的Bean,而是一個能生產或者修飾對象生成的工廠Bean,它的實現與設計模式中的工廠模式和修飾器模式類似 。

 2、BeanFactory容器的設計原理

以XmlBeanFactory的實現來講解IoC容器的設計原理。XmlBeanFactory設計的類繼承關係:

DefaultListableBeanFactory包含了基本IoC容器所具有的的重要功能,作爲Spring中的默認的功能完整的IoC容器來使用。XmlBeanFactory繼承DefaultListableBeanFactory,除了具備基本的功能之外,還具備讀取以XML文件定義的BeanDefinition的能力。XML文件是由XmlBeanFactory初始化的XmlBeanDefinitionReader來處理以XML方式定義的BeanDefinition。構造XmlBeanFactory這個IoC容器需要Resource類給出BeanDefinition的信息來源。Resource是Spring用來封裝I/O操作的類,例如ClassPathResource(“*.xml”)等。

XmlBeanFactory實現:

參考XmlBeanFactory的實現,以編程方式使用IoC容器:

  步驟:

1) 創建IoC配置文件的抽象資源,該象資源包含了BeanDefinition的定義信息。

2) 創建一個BeanFactory,這裏使用DefaultListableBeanFactory。

3) 創建一個載入BeanDefinition的讀入器,這裏使用XmlBeanDefinitionReader來載入XML文件形式的BeanDefinition,通過一個回調配置給BeanFactory。

4) 從定義好的資源位置讀入配置信息,具體的解析過程由XmlBeanDefinitionReader來完成。完成整個載入和註冊Bean定義之後,需要的IoC容器就建立起來了。這個時候就可以直接使用IoC容器了。

3、ApplicationContext的應用場景

ApplicationContext的接口關係:

 這些功能爲ApplicationContext 提供了一些BeanFactory 所不具備的新特性:

1)支持不同的信息源。ApplicationContext 擴展了MessageResource 接口,這些信息源的功能可以支持國際化的實現。

2)訪問資源。這一特性體驗在Resource 與ResourceLoader 上,這樣一來,我們可以從不同的地方得到Bean 定義資源。

3)支持應用事件。繼承了接口ApplicationEventPublisher,從而在上下文中引入了事件機制。這些事件和Bean 的生命週期的結合爲Bean 的管理提供了便利。

4)在ApplicationContext 中提供的附加服務。這些服務使得基本的IoC 容器的基本功能更加豐富。

 4、ApplicationContext 容器的設計原理

以FileSystemXmlApplicationContext 來說明ApplicationContext 容器的設計原理。

ApplicationContext 主要功能在FileSystemXmlApplicationContext 的基類AbstractXmlApplicationContext中已經實現了,在FileSystemXmlApplicationContext 中,作爲一個具體的應用上下文,只需要實現和它自身設計相關的2個功能。

一個功能是,如果應用直接使用FileSystemXmlApplicationContext ,對於實例化這個應用上下文的支持,同時啓動IoC容器的refresh()過程。

 

另外一個功能是與FileSystemXmlApplicationContext 設計具體相關的功能,這部分與怎樣從文件系統中加載XML的bean定義資源有關。

 

 2.3 IoC容器初始化過程

IoC 容器的初始化過程是通過refresh() 方法來啓動的,該方法標識着IoC 容器正式啓動。啓動過程包括:BeanDefinition 的Resource 定位、載入和註冊三個基本過程。Spring 把這三個過程分開,並使用不同的模塊來完成,從而方便自己定義IoC 容器的初始化過程。

       1、BeanDefinition的Resource 的定位BeanDefinition 的資源定位,它由ResourceLoader 通過統一的Resource 接口來完成。對於BeanDefinition 的存在形式,可以是文件系統中的通過FileSystemResource 來進行抽象;類路徑中定義的Bean 信息可以通過ClassPathResource 來進行抽象。這個定位過程類似於容器尋找數據的過程

       2、BeanDefinition 的載入載入過程是把用戶定義好的Bean 定義成IoC 容器內部的數據結構BeanDefinition。BeanDefinition 實際上就是POJO 對象在IoC 容器中的抽象,通過這個BeanDefinition 定義的數據結構,使IoC 容器能夠方便地管理Bean。

        3、BeanDefinition的註冊。註冊過程是通過BeanDefinitionRegistry 接口的實現來完成的。註冊過程把載入過程中解析得到BeanDefinition 向IoC 容器進行註冊。在IoC 容器內部將BeanDefinition 注入到一個HashMap 中去,IoC 容器就是通過這個HashMap 來持有這些Bean 數據的。

        以上IoC 容器的初始化過程,並不包含Bean 依賴注入的實現。在Spring IoC 的設計中,Bean 定義的載入和依賴注入是兩個獨立的過程。依賴注入一般發生在第一個通過getBean() 向容器中獲取Bean 的時候,也並不全是這樣,例如某個Bean 設置了lazyinit 屬性,那麼Bean 的依賴注入在其初始化的過程中就已經完成了,而不需要等到整個容器初始化完成之後,第一次使用getBean()纔會觸發

2.3.1 BeanDefinition 的Resource 定位

以編程的方式使用DefaultListableBeanFactory時,首先定義一個Resource來定位容器使用的BeanDefinition。使用ClassPathResource,意味着Spring會在類路徑中去尋找以文件形式存在的BeanDefinition信息 。ClassPathResource res = new ClassPathResource("beans.xml"); 

這裏定義的Resource並不能由DefaultListableBeanFactory直接使用,Spring通過BeanDefinitionReader來對這些信息進行處理。回到常用的ApplicationContext上,例如:FileSystemXmlApplicationContext、ClassPathXmlApplicationContext、XmlWebApplicationContext等。簡單地從這些類的名字上分析,可以清楚地看到它們可以提供哪些不同的Resource讀入功能。

以FileSystemXmlApplicationContext 爲例,來分析ApplicationContext 的實現是如何完成Resource 定位的,下面是這個類對應的繼承體系。

下面從源碼角度,更爲詳細的繼承體系:

從上圖中可以看出FileSystemXmlApplicationContext 已經通過繼承AbstractApplicationContext 具備了ResourceLoader 的功能,因爲AbstractApplicationContext 繼承自DefaultResourceLoader。下面是其源代碼:

對BeanDefinition 資源定位的過程,是由refresh() 方法觸發的,refresh() 方法的調用是在FileSystemXmlApplicationContext 的構造函數中啓動的。下圖可清楚的看出整個BeanDefinition資源定位的過程:

資源定位時序圖如下:

在BeanDefinition 定位完成基礎上,就可通過返回的Resource 對象進行BeanDefinition 載入。在完成定位過程後,爲BeanDefinition 的載入創造了I/O 操作的條件,但具體的數據還沒有開始讀入,讀入將在BeanDefinition的載入和解析中完成

 2.3.2 BeanDifinition的載入和解析

完成BeanDefinition的Resource定位的後,便可進行BeanDefinition信息的載入過程。該過程相當於把定義的BeanDefinition在IoC容器中轉化成一個Spring內部表示的數據結構的過程IoC容器對Bean的管理和依賴注入功能的實現,是通過對其持有的BeanDefinition進行各種相關操作來完成的。這些BeanDefinition數據在IoC容器中通過一個HashMap來保持和維護

從DefaultListableBeanFactory的設計入手來看IoC容器是怎樣完成BeanDefinition載入的。先回到IoC容器的初始化入口refresh()方法。該方法最初是在FileSystemXmlApplicationContext的構造函數中被調用的,它的調用標誌着容器初始化的開始,這些初始化對象就BeanDefinition數據,初始化入口函數:

IoC容器的refresh過程代碼:

FileSystemXmlApplicationContext 是怎麼完成信息的讀入的呢?讀入器的配置,可以在FileSystemXmlApplicationContext 的基類AbstractRefreshableApplicationContext 中的refreshBeanFactory() 方法的實現中查看。refreshBeanFactory() 方法被FileSystemXmlApplicationContext 構造函數中的refresh() 方法所調用。在該方法中,通過createBeanFactory() 創建一個DefaultListableBeanFactory 的IoC 容器供ApplicationContext 使用。同時,還啓動了loadBeanDefinitions() 來載入BeanDefinition

AbstractRefreshableApplicationContext 中的refreshBeanFactory() :

 

AbstractXmlApplicationContext的loadBeanDefinitions():

XmlBeanDefinitionReader載入BeanDefinition(),代碼清單如下:

AbstractBeanDefinitionReader載入BeanDefinition:

調用的loadBeanDefinitions(Resource res)方法在AbstractBeanDefinitionReader類裏是沒有實現的,具體的實現在XmlBeanDefinitionReader中。在讀取器中,需要得到封裝了對XML文件的I/O操作的代表XML文件的Resource,讀取器可以在打開I/O流後得到XML的文件對象。有了這個文件對象以後,就可按照Spring的Bean定義規則來對這個XML的文檔樹進行解析了,該解析是交給BeanDefinitionParserDelegate來完成的,  對BeanDefinition的載入實現:

 

BeanDefinition載入時序圖:

完成資源的載入後,需要將載入的BeanDefinition進行解析並轉化爲IoC容器內部數據結構,這個過程在registerBeanDefinitions()成,具體是由BeanDefinitionsDocumentReader來完成。

BeanDefinition的載入分成兩部分:首先,通過調用XML的解析器得到document對象。然後,按照Spring的Bean規則進行解析。按照Spring的Bean規則進行解析的過程是在默認設置好的DefaultBeanDefinitionDocumentReader中實現的。DefaultBeanDefinitionDocumentReader的創建是在後面的方法中完成的,然後再完成BeanDefinition的處理,處理的結果由BeanDefinitionHolder對象持有。這個BeanDefinitionHolder除了持有BeanDefinition對象外,還持有其他與BeanDefinition的使用相關的信息,比如Bean的名字、別名集合等。這個BeanDefinitionHolder的生成是通過對Document文檔樹的內容進行解析來完成的,可看到該解析過程是由BeanDefinitionParserDelegate實現的(processBeanDefinition方法中實現)的,同時這個解析是與Spring對BeanDefinition的配置規則緊密相關的。

 創建BeanDefinitionDocumentReader:

具體的Spring BeanDefinition的解析是在BeanDefinitionParserDelegate中完成的:


上面介紹了對Bean元素進行解析的過程,也就是BeanDefinition依據XML的定義被創建的過程。這個BeanDefinition可以看成是對定義的抽象。這個數據對象中封裝的數據大多都是與定義相關的,也有很多就是我們在定義Bean時看到的那些Spring標記,比如常見的init-method、destroy-method、factory-method等。

beanClass、description、lazyInit這些屬性都是在配置bean時經常碰到的,都集中在BeanDefinition。通過解析以後,便可對BeanDefinition元素進行處理,在這個過程中可以看到對Bean定義的相關處理,比如對元素attribute值的處理,對元素屬性值的處理,對構造函數設置的處理,等等。對BeanDefinition定義元素的處理,代碼如下:


上面是具體生成BeanDefinition的地方。在這裏,我們舉一個對property進行解析的例子來完成對整個BeanDefinition載入過程的分析,還是在類BeanDefinitionParserDelegate的代碼中,一層一層地對BeanDefinition中的定義進行解析,比如從屬性元素集合到具體的每一個屬性元素,然後纔是對具體的屬性值的處理。根據解析結果,對這些屬性值的處理會被封裝成PropertyValue對象並設置到BeanDefinition對象中去,對BeanDefinition中Property元素集合的處理。

2.3.3 BeanDefinition在IoC容器中的註冊

BeanDefinition在IoC容器中載入和解析的過程完成以後,用戶定義的BeanDefinition信息已經在IoC容器內建立起了自己的數據結構以及相應的數據表示,但此時這些數據還不能供IoC容器直接使用,需要在IoC容器中對這些BeanDefinition數據進行註冊。在DefaultListableBeanFactory中,是通過一個HashMap來持有載入的BeanDefinition的,這個HashMap的定義在DefaultListableBeanFactory中可以看到,如下所示。

 private final Map beanDefinitionMap = new  ConcurrentHashMap();

 將解析得到的BeanDefinition向IoC容器中的beanDefinitionMap註冊的過程是在載入BeanDefinition完成後進行的,註冊的調用過程如圖所示:

在DefaultListableBeanFactory中實現了BeanDefinitionRegistry的接口,該接口的實現完成BeanDefinition向容器的註冊。註冊過程就是把解析得到的BeanDefinition設置到 hashMap中去。需要注意的是,如果遇到同名的BeanDefinition,進行處理的時候需要依據allowBeanDefinitionOverriding的配置來完成。

 

完成了BeanDefinition的註冊,就完成了IoC容器的初始化過程。此時,在使用的IoC容器DefaultListableBeanFactory中已經建立了整個Bean的配置信息,而且這些BeanDefinition已經可以被容器使用了,它們都在beanDefinitionMap裏被檢索和使用。容器的作用就是對這些信息進行處理和維護

2.4 IoC容器的依賴注入

當前IoC容器已經載入了用戶定義的Bean信息,用戶第一次向IoC容器索要Bean時,即調用getBean()的接口定義,觸發依賴注入。下面從DefaultListableBeanFactory的基類AbstractBeanFactory入手去看看getBean的實現, getBean觸發的依賴注入,代碼如下:


 

 

依賴注入的大致過程:

 

getBean是依賴注入的起點,之後會調用createBean,下面通過createBean代碼來了解這個實現過程。在這個過程中,Bean對象會依據BeanDefinition定義的要求生成。在AbstractAutowireCapableBeanFactory中實現了這個createBean,createBean不但生成了需要的Bean,還對Bean初始化進行了處理,比如實現了在BeanDefinition中的init-method屬性定義,Bean後置處理器等。 AbstractAutowireCapableBeanFactory中的createBean具體的過程如代碼如下:


 

與依賴注入關係特別密切的方法有createBeanInstance()和populateBean()。在createBeanInstance()中生成了Bean所包含的Java對象,這個對象的生成有很多種不同的方式,可以通過工廠方法生成,也可以通過容器的autowire特性生成,這些生成方式都是由相關的BeanDefinition來指定的。 Bean包含的Java對象的生成,代碼如下:

 

這裏用CGLIB對Bean進行實例化,IoC容器中,要了解怎樣使用cglib來生成Bean對象,需要看一下SimpleInstantiationStrategy類。這個Strategy是Spring用來生成Bean對象的默認類,它提供了兩種實例化Java對象的方法,一種是通過BeanUtils,它使用了JDK的反射功能,一種是通過前面提到的CGLIB來生成, 使用SimpleInstantiationStrategy生成Java對象,代碼如下:

以上分析了實例化Bean對象的整個過程。在實例化Bean對象生成的基礎上,怎樣把這些Bean對象的依賴關係設置好,完成整個依賴注入過程。這個過程涉及對各種Bean對象的屬性的處理過程(即依賴關係處理的過程),這些依賴關係處理的依據就是已經解析得到的BeanDefinition。要詳細瞭解這個過程,需要回到前面的populateBean()方法,這個方法在AbstractAutowireCapableBeanFactory中的populateBean()實現如代碼如下:

 

 

 

 

這裏通過使用BeanDefinitionResolver來對BeanDefinition進行解析然後注入到property中。下面到BeanDefinitionValueResolver中去看一下解析過程的實現,以對Bean reference進行解析爲例具體的對Bean reference進行解析的過程如代碼清單2-28所示:

 

這兩種屬性的注入都調用了resolveValueIfNecessary這個方法包含了所有對注入類型的處理。下面看一下resolveValueIfNecessary的實現,如代碼清單如下:

 

 

 

 

在完成這個解析過程後,已經爲依賴注入準備好了條件,這是真正把Bean對象設置到它所依賴的另一個Bean的屬性中去的地方,其中處理的屬性是各種各樣的。依賴注入的發生是在BeanWrapper的setPropertyValues中實現的,具體的完成卻是在BeanWrapper的子類BeanWrapperImpl中實現的,如代碼清單如下:

 

 

 

 

這樣就完成了對各種Bean屬性的依賴注入過程。在Bean的創建和對象依賴注入的過程中,需要依據BeanDefinition中的信息來遞歸地完成依賴注入。從上面的幾個遞歸過程中可以看到,這些遞歸都是以getBean爲入口的。一個遞歸是在上下文體系中查找需要的Bean和創建Bean的遞歸調用;另一個遞歸是在依賴注入時,通過遞歸調用容器的getBean方法,得到當前Bean的依賴Bean,同時也觸發對依賴Bean的創建和注入。在對Bean的屬性進行依賴注入時,解析的過程也是一個遞歸的過程。這樣,根據依賴關係,一層一層地完成Bean的創建和注入,直到最後完成當前Bean的創建。有了這個頂層Bean的創建和對它的屬性依賴注入的完成,意味着和當前Bean相關的整個依賴鏈的注入也完成了。

在Bean創建和依賴注入完成以後,在IoC容器中建立起一系列依靠依賴關係聯繫起來的Bean,這個Bean已經不是簡單的Java對象了。該Bean系列以及Bean之間的依賴關係建立完成以後,通過IoC容器的相關接口方法,就可以非常方便地供上層應用使用了。

2.5 容器其他相關特性的設計與實現

2.5.1 ApplicationContext和Bean的初始化及銷燬

對於BeanFactory,特別是ApplicationContext,容器自身也有一個初始化和銷燬關閉的過程。下面詳細看看在這兩個過程中,應用上下文完成了什麼,可以讓我們更多地理解應用上下文的工作,容器初始化和關閉過程可以簡要地通過下圖表現:

從圖中可以看到,對ApplicationContext啓動的過程是在AbstractApplicationContext中實現的。在使用應用上下文時需要做一些準備工作,這些準備工作在prepareBeanFactory()方法中實現。在這個方法中,爲容器配置了ClassLoader、PropertyEditor和BeanPostProcessor等,從而爲容器的啓動做好了必要的準備工作。

 

同樣,在容器要關閉時,也需要完成一系列的工作,這些工作在doClose( )方法中完成。在這個方法中,先發出容器關閉的信號,然後將Bean逐個關閉,最後關閉容器自身。

 

以上是容器的初始化和銷燬的設計與實現。在這個過程中需要區分Bean的初始化和銷燬過程。Spring IoC容器提供了相關的功能,可以讓應用定製Bean的初始化和銷燬過程。容器的實現是通過IoC管理Bean的生命週期來實現的。Spring IoC容器在對Bean的生命週期進行管理時提供了Bean生命週期各個時間點的回調。在分析Bean初始化和銷燬過程的設計之前,簡要介紹一下IoC容器中的Bean生命週期:

Bean實例的創建->爲Bean實例設置屬性->調用Bean的初始化方法->通過IoC容器使用Bean ->容器關閉時調用Bean的銷燬方法 

Bean的初始化方法調用是在以下的initializeBean方法中實現的:

 

在調用Bean的初始化方法之前,會調用一系列的aware接口實現,把相關的BeanName、BeanClassLoader,以及BeanFactoy注入到Bean中去。接着會看到對invokeInitMethods的調用,這時還會看到啓動afterPropertiesSet的過程,當然,這需要Bean實現InitializingBean的接口,對應的初始化處理可以在InitializingBean接口的afterPropertiesSet方法中實現,這裏同樣是對Bean的一個回調。

最後,還會看到判斷Bean是否配置有initMethod,如果有,那麼通過invokeCustomInitMethod方法來直接調用,最終完成Bean的初始化。

 在這個對initMethod的調用中,可以看到首先需要得到Bean定義的initMethod,然後通過JDK的反射機制得到Method對象,直接調用在Bean定義中聲明的初始化方法。

與Bean初始化類似,當容器關閉時,可以看到對Bean銷燬方法的調用。Bean銷燬過程是這樣的:

 

其中的destroy方法,對Bean進行銷燬處理。最終在DisposableBeanAdapter類中可以看到destroy方法的實現。

 

 

這裏可以看到對Bean的銷燬過程,首先對postProcessBeforeDestruction進行調用,然後調用Bean的destroy方法,最後是對Bean的自定義銷燬方法的調用,整個過程和前面的初始化過程很類似。

 2.5.2 lazy-init屬性和預實例化

通過設置Bean的lazy-init屬性來控制預實例化的過程,預實例化在初始化容器時完成Bean的依賴注入。這種容器的使用方式會對容器初始化的性能有一些影響,但卻能夠提高應用第一次取得Bean的性能。

在refresh()中的代碼實現,可以看到預實例化是整個refresh初始化IoC容器的一個步驟。在finishBeanFactoryInitialization的方法中,封裝了對lazy-init屬性的處理,實際的處理是在DefaultListableBeanFactory這個基本容器的preInstantiateSingletons方法中完成的。該方法對單件Bean完成預實例化,這個預實例化的完成巧妙地委託給容器來實現。如果需要預實例化,那麼就直接在這裏採用getBean去觸發依賴注入,與正常依賴注入的觸發相比,只有觸發的時間和場合不同。refresh()中的預實例化代碼如下:

 

2.5.3 FactoryBean的實現

FactoryBean爲應用生成需要的對象,這些對象往往是經過特殊處理的,如ProxyFactoryBean這樣的特殊Bean。FactoryBean的生產特性是在getBean中起作用的,看下面的調用:

bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); 

getObjectForBeanInstance做了哪些處理?在getObjectForBeanInstance的實現方法中可以看到在FactoryBean中常見的getObject方法的接口,詳細的實現過程如代碼如下所示:

 

 

 2.5.4 BeanPostProcessor的實現

BeanPostProcessor是使用IoC容器時經常會遇到的一個特性,這個Bean的後置處理器是一個監聽器,它可以監聽容器觸發的事件。將它向IoC容器註冊後,容器中管理的Bean具備了接收IoC容器事件回調的能力。BeanPostProcessor的使用非常簡單,只需要通過設計一個具體的後置處理器來實現。同時,這個具體的後置處理器需要實現接口類BeanPostProcessor,然後設置到XML的Bean配置文件中。這個BeanPostProcessor是一個接口類,它有兩個接口方法,一個是postProcessBeforeInitialization,在Bean初始化前提供回調入口;一個是postProcessAfterInitialization,在Bean的初始化後提供回調入口,這兩個回調的觸發都是和容器管理Bean的生命週期相關的。這兩個回調方法的參數都是一樣的,分別是Bean的實例化對象和Bean的名字:

對於這些接口是在什麼地方與IoC結合在一起的,可以查看一下以getBean方法爲起始的調用關係,其調用過程如圖所示:

 

postProcessBeforeInitialization是在populateBean完成之後被調用的。從BeanPostProcessor中的一個回調接口入手,對另一個回調接口postProcessAfterInitialization方法的調用,實際上也是在同一個地方封裝完成的,這個地方就是populateBean方法中的initializeBean調用。在前面對IoC的依賴注入進行分析時,對這個populateBean有過分析,這個方法實際上完成了Bean的依賴注入。在容器中建立Bean的依賴關係,是容器功能實現的一個很重要的部分。節選doCreateBean中的代碼就可以看到postProcessBeforeInitialization調用和populateBean調用的關係,如下所示。

 

具體的初始化過程也是IoC容器完成依賴注入的一個重要部分。在initializeBean方法中,需要使用Bean的名字,完成依賴注入以後的Bean對象,以及這個Bean對應的BeanDefinition。在這些輸入的幫助下,完成Bean的初始化工作,這些工作包括爲類型是BeanNameAware的Bean設置Bean的名字,類型是BeanClassLoaderAware的Bean設置類裝載器,類型是BeanFactoryAware的Bean設置自身所在的IoC容器以供回調使用,當然,還有對postProcessBeforeInitialization/postProcessAfterInitialization的回調和初始化屬性init-method的處理等。經過這一系列的初始化處理之後,得到的結果就是可以正常使用的由IoC容器託管的Bean了。IoC容器對Bean的初始化,代碼如下:

 

2.5.5   autowiring(自動依賴裝配)的實現 

在前面對IoC容器實現原理的分析中,一直是通過BeanDefinition的屬性值和構造函數以顯式的方式對Bean的依賴關係進行管理的。在Spring IoC容器中還提供了自動依賴裝配的方式。在自動裝配中,不需要對Bean屬性做顯式的依賴關係聲明,只需要配置好autowiring屬性,IoC容器會根據這個屬性的配置,使用反射自動查找屬性的類型或者名字,然後基於屬性的類型或名字來自動匹配IoC容器中的Bean,從而自動地完成依賴注入。

autowiring屬性是在對Bean屬性進行依賴注入時起作用。對autowirng屬性進行處理,從而完成對Bean屬性的自動依賴裝配,是在populateBean中實現的。對屬性autowiring的處理是populateBean處理過程的一個部分。在populateBean的實現中,在處理一般的Bean之前,先對autowiring屬性進行處理。如果當前的Bean配置了autowire_by_name和autowire_by_type屬性,那麼調用相應的autowireByName方法和autowireByType方法。例如,對於autowire_by_name,它首先通過反射機制從當前Bean中得到需要注入的屬性名,然後使用這個屬性名向容器申請與之同名的Bean,這樣實際又觸發了另一個Bean的生成和依賴注入的過程。populateBean對autowiring屬性的處理,代碼如下:

在對autowiring類型做了一些簡單的邏輯判斷以後,通過調用autowireByName和autowireByType來完成自動依賴裝配。以autowireByName爲例子來看看容器的自動依賴裝配功能是怎樣實現的。對autowireByName來說,它首先需要得到當前Bean的屬性名,這些屬性名已經在BeanWrapper和BeanDefinition中封裝好了,然後是對這一系列屬性名進行匹配的過程。在匹配的過程中,因爲已經有了屬性的名字,所以可以直接使用屬性名作爲Bean名字向容器索取Bean,這個getBean會觸發當前Bean的依賴Bean的依賴注入,從而得到屬性對應的依賴Bean。在執行完這個getBean後,把這個依賴Bean注入到當前Bean的屬性中去,這樣就完成了通過這個依賴屬性名自動完成依賴注入的過程。autowireByType的實現和autowireByName的實現過程是非常類似的,感興趣的讀者可以自己進行分析。這些autowiring的實現如代碼所示:

 

2.5.6   Bean的依賴檢查 

在使用Spring的時候,如果應用設計比較複雜,那麼在這個應用中, IoC管理的Bean的個數可能非常多,這些Bean之間的相互依賴關係也會非常複雜。在一般情況下,Bean的依賴注入是在應用第一次向容器索取Bean的時候發生,在這個時候,不能保證注入一定能夠成功,如果需要重新檢查這些依賴關係的有效性,會是一件很繁瑣的事情。爲了解決這樣的問題,在Spring IoC容器中,設計了一個依賴檢查特性,通過它,Spring可以幫助應用檢查是否所有的屬性都已經被正確設置。在具體使用的時候,應用只需要在Bean定義中設置dependency-check屬性來指定依賴檢查模式即可,這裏可以將屬性設置爲none、simple、object、all四種模式,默認的模式是none。如果對檢查模式進行了設置,通過下面的分析,可以更好地理解這個特性的使用。具體的實現代碼是在AbstractAutowireCapableBeanFactory實現createBean的過程中完成的。在這個過程中,會對Bean的Dependencies屬性進行檢查,如果發現不滿足要求,就會拋出異常通知應用。

2.5.7   Bean對IoC容器的感知

容器管理的Bean一般不需要了解容器的狀態和直接使用容器,但在某些情況下,是需要在Bean中直接對IoC容器進行操作的,這時候,就需要在Bean中設定對容器的感知。Spring IoC容器也提供了該功能,它是通過特定的aware接口來完成的。aware接口有以下這些:

BeanNameAware ,可以在Bean中得到它在IoC容器中的Bean實例名稱。

BeanFactoryAware,可以在Bean中得到Bean所在的IoC容器,從而直接在Bean中使用IoC容器的服務。

ApplicationContextAware,可以在Bean中得到Bean所在的應用上下文,從而直接在Bean中使用應用上下文的服務。

MessageSourceAware,在Bean中可以得到消息源。

ApplicationEventPublisherAware,在Bean中可以得到應用上下文的事件發佈器,從而可以在Bean中發佈應用上下文的事件。

ResourceLoaderAware,在Bean中可以得到ResourceLoader,從而在Bean中使用ResourceLoader加載外部對應的Resource資源。

在設置Bean的屬性之後,調用初始化回調方法之前,Spring會調用aware接口中的setter方法。以ApplicationContextAware爲例,分析對應的設計和實現。這個接口定義得很簡單。

這裏只有一個方法setApplicationContext(ApplicationContext applicationContext),它是一個回調函數,在Bean中通過實現這個函數,可以在容器回調該aware接口方法時使注入的applicationContext引用在Bean中保存下來,供Bean需要使用ApplicationContext的基本服務時使用。這個對setApplicationContext方法的回調是由容器自動完成的。可以看到,一個ApplicationContextAwareProcessor作爲BeanPostProcessor的實現,對一系列的aware回調進行了調用,比如對ResourceLoaderAware接口的調用,對ApplicationEventPublisherAware接口的調用,以及對MessageSourceAware和ApplicationContextAware的接口調用等。

 

而作爲依賴注入的一部分,postProcessBeforeInitialization會在initializeBean的實現過程中被調用,從而實現對aware接口的相關注入。關於initializeBean的詳細過程,感興趣的讀者可以參閱前面的章節進行回顧。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章