1. 程式人生 > >Spring5原始碼分析系列(四)Spring5原始碼分析2

Spring5原始碼分析系列(四)Spring5原始碼分析2

本文緊接上文Spring5原始碼分析1,講解基於XML的依賴注入,文章參考自Tom老師視訊,下一篇文章將介紹基於Annotation的依賴注入。

基於XML的依賴注入

1、依賴注入發生的時間

當SpringIOC容器完成了Bean定義資源的定位、載入和解析註冊以後,IOC容器中已經管理類Bean定義的相關資料,但是此時IOC容器還沒有對所管理的Bean進行依賴注入,依賴注入在以下兩種情況發生:

(1).使用者第一次通過getBean方法向IOC容索要Bean時,IOC容器觸發依賴注入。

(2).當用戶在Bean定義資源中為<bean>元素配置了lazy-init屬性,即讓容器在解析註冊Bean定義時進行預例項化,觸發依賴注入。

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

在BeanFactory中我們看到getBean(String...)函式,它的具體實現在AbstractBeanFactory中。

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

AbstractBeanFactory的getBean相關方法的原始碼如下:

通過上面對向IOC容器獲取Bean方法的分析,我們可以看到在Spring中,如果Bean定義的單例模式(Singleton),則容器在建立之前先從快取中查詢,以確保整個容器中只存在一個例項物件。如果Bean定義的是原型模式(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核心功能的控制反轉和依賴注入的相關功能。