基於 XML 的依賴注入

1.依賴注入發生的時間
(1).用戶第一次通過getBean方法向IOC容索要Bean時,IOC容器觸發依賴注入。
(2).當用戶在Bean定義資源中爲<bean>元素配置了lazy-init屬性,即讓容器在解析註冊Bean定義時進行預實例化,觸發依賴注入。

BeanFactory接口定義了SpringIOC容器的基本功能規範,是SpringIOC容器所應遵守的最底層和最基本的編程規範。BeanFactory接口中定義了幾個getBean方法,就是用戶向IOC容器索取管理的Bean的方法,我們通過分析其子類的具體實現,理解SpringIOC容器在用戶索取Bean時如何完成依賴注入。


BeanFactory 中我們看到 getBeanString...) 函數, 它的具體實現在 AbstractBeanFactory中。

2AbstractBeanFactory 通過 getBean IOC 容器獲取被管理的 Bean

AbstractBeanFactory getBean 相關方法的源碼如下:

通過上面對向IOC容器獲取Bean方法的分析,我們可以看到在Spring中,如果Bean定義的單例模式(Singleton),則容器在創建之前先從緩存中查找,以確保整個容器中只存在一個實例對象。如果定義的是原型模式(Prototype),則容器每次都會創建一個新的實例對象。除此之外,Bean定義還可以擴展爲指定其生命週期範圍。

上面的源碼只是定義了根據Bean定義的模式,採取的不同創建Bean實例對象的策略,具體的Bean實例對象的創建過程由實現了ObejctFactory接口的匿名內部類的createBean方法完成,ObejctFactory使用委派模式,具體的Bean實例創建過程交由其實現類AbstractAutowireCapableBeanFactory完成,我們繼續分析AbstractAutowireCapableBeanFactory的createBean方法的源碼,理解其創建Bean實例的具體實現過程。

3、AbstractAutowireCapableBeanFactory創建Bean實例對象:

AbstractAutowireCapableBeanFactory類實現了ObejctFactory接口,創建容器指定的Bean實例對象,同時還對創建的Bean實例對象進行初始化處理。其創建Bean實例對象的方法源碼:


通過對方法源碼的分析,我們看到具體的依賴注入實現在以下兩個方法中:
(1).createBeanInstance:生成Bean所包含的java對象實例。
(2).populateBean:對Bean屬性的依賴注入進行處理。

下面繼續分析這兩個方法的代碼實現。

4、createBeanInstance方法創建Bean的java實例對象:

在createBeanInstance方法中,根據指定的初始化策略,使用靜態工廠、工廠方法或者容器的自動裝配特性生成java實例對象,創建對象的源碼:


經過對上面的代碼分析,我們可以看出,對使用工廠方法和自動裝配特性的Bean的實例化相當比較清楚,調用相應的工廠方法或者參數匹配的構造方法即可完成實例化對象的工作,但是對於我們最常使用的默認無參構造方法就需要使用相應的初始化策略(JDK的反射機制或者CGLIB)來進行初始化了,在方法getInstantiationStrategy().instantiate()中就具體實現類使用初始策略實例化對象。
5、SimpleInstantiationStrategy類使用默認的無參構造方法創建Bean實例化對象:
在使用默認的無參構造方法創建Bean的實例化對象時,方法getInstantiationStrategy().instantiate()調用了SimpleInstantiationStrategy類中的實例化Bean的方法,其源碼:


通過上面的代碼分析, 我們看到了如果 Bean 有方法被覆蓋了, 則使用JDK 的反射機制進行實例化, 否

則, 使用CGLIB 進行實例化。instantiateWithMethodInjection 方 法 調 用 SimpleInstantiationStrategy 的 子 類CglibSubclassingInstantiationStrategy 使用 CGLIB 來進行初始化, 其源碼:


CGLIB是一個常用的字節碼生成器的類庫,它提供了一系列API實現java字節碼的生成和轉換功能。我們在學習JDK的動態代理時都知道,JDK的動態代理只能針對接口,如果一個類沒有實現任何接口,要對其進行動態代理只能使用CGLIB。

6、populateBean方法對Bean屬性的依賴注入:
在第3步的分析中我們已經瞭解到Bean的依賴注入分爲以下兩個過程:
(1).createBeanInstance:生成Bean所包含的Java對象實例。
(2).populateBean:對Bean屬性的依賴注入進行處理。

上面我們已經分析了容器初始化生成Bean所包含的Java實例對象的過程,現在我們繼續分析生成對象後,SpringIOC容器是如何將Bean的屬性依賴關係注入Bean實例對象中並設置好的,屬性依賴注入的代碼如下:


分析上述代碼,我們可以看出,對屬性的注入過程分以下兩種情況:
(1).屬性值類型不需要轉換時,不需要解析屬性值,直接準備進行依賴注入。

(2).屬性值需要進行類型轉換時,如對其他對象的引用等,首先需要解析屬性值,然後對解析後的屬性值進行依賴注入。 對屬性值的解析是在BeanDefinitionValueResolver類中的resolveValueIfNecessary方法中進行的,對屬性值的依賴注入是通過bw.setPropertyValues方法實現的,在分析屬性值的依賴注入之前,我們先分析一下對屬性值的解析過程。

7、BeanDefinitionValueResolver解析屬性值:

當容器在對屬性進行依賴注入時,如果發現屬性值需要進行類型轉換,如屬性值是容器中另一個Bean實例對象的引用,則容器首先需要根據屬性值解析出所引用的對象,然後才能將該引用對象注入到目標實例對象的屬性上去,對屬性進行解析的由resolveValueIfNecessary方法實現,其源碼:


通過上面的代碼分析,我們明白了Spring是如何將引用類型,內部類以及集合類型等屬性進行解析的,屬性值解析完成後就可以進行依賴注入了,依賴注入的過程就是Bean對象實例設置到它所依賴的Bean對象屬性上去,在第6步中我們已經說過,依賴注入是通過bw.setPropertyValues方法實現的,該方法也使用了委託模式,在BeanWrapper接口中至少定義了方法聲明,依賴注入的具體實現交由其實現類BeanWrapperImpl來完成,下面我們就分析依BeanWrapperImpl中賴注入相關的源碼。

8、BeanWrapperImpl對Bean屬性的依賴注入

BeanWrapperImpl類主要是對容器中完成初始化的Bean實例對象進行屬性的依賴注入,即把Bean對象設置到它所依賴的另一個Bean的屬性中去。然而,BeanWrapperImpl中的注入方法實際上由AbstractNestablePropertyAccessor來實現的,其相關源碼:

通過對上面注入依賴代碼的分析,我們已經明白了SpringIOC容器是如何將屬性的值注入到Bean實例對象中去的:
(1).對於集合類型的屬性,將其屬性值解析爲目標類型的集合後直接賦值給屬性。

(2).對於非集合類型的屬性,大量使用了JDK的反射和內省機制,通過屬性的getter方法(readerMethod)獲取指定屬性注入以前的值,同時調用屬性的setter方法(writerMethod)爲屬性設置注入後的值。看到這裏相信很多人都明白了Spring的setter注入原理。

至此SpringIOC容器對Bean定義資源文件的定位,載入、解析和依賴注入已經全部分析完畢,現在SpringIOC容器中管理了一系列靠依賴關係聯繫起來的Bean,程序不需要應用自己手動創建所需的對象,SpringIOC容器會在我們使用的時候自動爲我們創建,並且爲我們注入好相關的依賴,這就是Spring核心功能的控制反轉和依賴注入的相關功能。


上一篇: 自定義標籤源碼分析

下一篇: 基於Annotation的依賴注入

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