Spring底層實現之源碼解讀

做為Java開源世界的第一框架,Spring已經成為事實上的Java EE開發標準Spring框架最根本的使命是簡化Java開發,因此學習、研究、掌握Spring框架成為每一位Java開發人員的必修課。而閱讀源碼則是學習Spring的最好方式之一。

Spring裡面最重要的特性就是Ioc,可能你還會說aop。其實aop的實現也是基於ioc。Ioc(Inversion of Control),即“控制反轉”,不是什麼技術,而是一種設計思想。在Java開發中,Ioc意味著將你設計好的對象交給容器控制,而不是傳統的在你的對象內部直接控制。

關於Spring IOC源碼分析的文章網上很多,現在我就來另闢蹊徑。Spring Ioc的對象扭轉以及涉及到的核心接口來分析一下它的源碼實現。

我把Spring Ioc的對象轉換分為以下4個步驟:

Resource -> BeanDefinition -> BeanWrapper -> Object

1 Resource

Spring底層實現之源碼解讀

Resouce 其實是一個接口,代表的是資源,在計算機裡面從一個地方移動到另外一個地方所需要的東西就是數據流,所以 Resource 實現了 InputStreamSource 接口,通過 InputStreamSource 接口可以獲取到 Inputstream,這樣就可以讀取不同的 Bean 定義了。

public interface InputStreamSource {

InputStream getInputStream() throws IOException;

}

Spring可以定義不同類型的bean,最後都可以封裝成Resource通過IO流進行讀取。 Spring可以定義類型的bean對象:

  • XML:這是Spring最開始定義bean的形式
  • Annotation :由於通過XML定義bean的繁瑣,Spring進行了改進可以通過@Component以及基於它的註解來定義bean。例如:@Service,@Controller等等,它們都可以定義bean ,只不過語義更加明確。
  • Class:通過@Configuration與@Bean註解定義,@Configuration代理xml資源文件,而@Bean代替<bean>標籤。/<bean>
  • Properties/yml:通過 @EnableConfigurationProperties 與 @ConfigurationProperties 來定義bean。這種形式在Spring boot自動注入裡面大量使用。

2、BeanDefinition

望文生義,很顯示這個是 Bean 對象的定義。 Spring通過不同形式來定義bean,最終會把這些定義轉化成BeanDefinition 保存在Spring容器當中進行依賴注入。下面我們來看一下BeanDefinition的接口定義。

Spring底層實現之源碼解讀

這個接口的定義很複雜但是,對於初始理解spring ioc,只需要關心兩個方法。

  • getConstructorArgumentValues:獲取構造器注入定義的參數。
  • getPropertyValues:獲取setter注入定義的參數。

所以Spring支持構造器注入與setter依賴注入。

Spring底層實現之源碼解讀

3、BeanWapper

其實什麼是依賴注入,簡單來說就是Spring幫我們創建對象。把創建對象寫死在Java文件變成了通過不同的Spring配置可以注入不同的值。創建對象的職責由Java文件變成了Spring配置文件。

下面我就問一個簡單的問題,如何創建對象。可能大家呵呵一笑,創建對象這還不簡單。

Spring底層實現之源碼解讀

其實 Spring 也是這樣來創建對象的,不信講看 : (入口方法 BeanFactory#getBean)

  1. AbstractAutowireCapableBeanFactory#createBeanInstance() :通過反射Constructor調用配置的無參或者有參來創建對象實例。通過BeanDefinition#getConstructorArgumentValues 獲取,並返回 BeanWrapper 對象。
  2. AbstractAutowireCapableBeanFactory#populateBean():,獲取到定義的bean生成的所有的定義的setter注入的屬性(BeanDefinition#getPropertyValues),然後遍歷些這些屬性,通過內省獲取到對象所有的 屬性描述器(PropertyDescriptor),獲取到,屬性的PropertyDescriptor#getWriteMethod方法,也就是setter方法,依賴注入值。如果是普通屬性或者一些複雜對象,比如普通屬性String, int, long或者classpath:*轉換為Resource複雜對象等等,直接注入即可;對於引用類型對象,繼續依賴注入直到所有的屬性是普通屬性為止。
  3. AbstractAutowireCapableBeanFactory#initializeBean():調用 Spring 自定義的初始化方法比如:BeanPostProcessor 擴展以及 init-method。

實例化對象返回BeanWrapper,其實是為了依賴注入服務也就是上面的第二步。 這個接口的功能還是很複雜的,它繼承了 4 個接口。

  • TypeConverter
  • PropertyEditorRegistry
  • PropertyAccessor
  • ConfigurablePropertyAccessor

下面就來分別介紹一下這些接口的功能。

3.1 TypeConverter

下面就是這個接口的定義。

Spring底層實現之源碼解讀

它的作用就是自動類型轉換,因為Spring作得太無感知了。你也許還沒有感覺到它的存在。沒關係,我提示一下你應該就會明白。比如,聲明一個用戶對象這個對象既有String類型的名稱,又有Int類型的年齡。Spring怎麼知道屬性的類型呢?這個就是 Spring的自動類型轉換。關於Spring的自動類型轉換 我在之前就已經分析過了。

3.2 PropertyEditorRegistry

這個接口主要的作用是註冊屬性修改器(PropertyEditor),這個是 Java 內省裡面的機制。

Spring底層實現之源碼解讀

一般通過繼承 java.beans.PropertyEditorSupport 來實現自定義的類型轉換。在 Spring 內部有大量的實現,如下圖所示

Spring底層實現之源碼解讀

3.3 PropertyAccessor

Spring底層實現之源碼解讀

PropertyAccessor這個接口是判斷對象中的某個屬性是否可讀/可寫,並且可以定入或者讀取某個屬性的值。從這個接口定義我們可以看出,它的使用其實就是真正用來依賴注入的。然後調用屬性操作的寫入操作,完全依賴注入。

3.4 ConfigurablePropertyAccessor

Spring底層實現之源碼解讀

這個接口的功能和PropertyEditorRegistry接口一樣,只不過後者是通過java內省來進行類型自動轉換,而ConfigurablePropertyAccessor接口是通過Spring自己定義的org.springframework.core.convert.ConversionService來作類型轉換類型轉換。在Spring中默認使用的DefaultConversionService來作自動類型轉換支持,並且內部還添加了很多默認的類型轉換。

Spring底層實現之源碼解讀

Spring底層實現之源碼解讀

Spring底層實現之源碼解讀

Spring底層實現之源碼解讀

關於Spring的自動類型轉換 我在之前就已經分析過了。和java內省的原理是一樣的。

3.5 BeanWrapper

Spring底層實現之源碼解讀

這個接口就挺簡單了,通過實現了上面的幾個接口具有了依賴注入、類型轉換註冊(java 內省或者Spring自定義的自動類型轉換)。然後這個接口的主要的作用就是通過調用getWrappedInstance方法獲取到當前實例對象,提供給屬性的writer方法進行依賴注入。

writeMethod.invoke(getWrappedInstance(), value);

  • 1

4、總結

讓我們再來看一下Spring的對象扭轉過過程:

Resource -> BeanDefinition -> BeanWrapper -> Object

相信基於以上的講解,大家對於上面的過程能夠理解Spring IOC的項目過程。在Spring 進行 依賴注入的時候,首先把這種資源轉化成Resource抽象,通過裡面的IO流讀取定義的bean。然後再轉化成BeanDefinitioin,裡面定義了包括構造器注入,以及setter注入的定義。最後通過BeanWrapper這個接口,首先獲取定義的構造器注入屬性,通過反射中的Constructor來創建對象。基於這個對象,通過java裡面的內省機制獲取到定義屬性的屬性描述器(PropertyDescriptor),調用屬性的寫入方法完成依賴注入,最後再調用Spring的自定義初始化邏輯,主要包括以下三個擴展點:

  • BeanPostProcess,Spring aop就是基於此擴展。
  • Init-method,可以在 bean 標籤通過 init-method定義,也可以實現InitializingBean
  • XXXAware,Spring容器感知類,可以在bean裡面獲取到 Spring容器的內部屬性。
Spring底層實現之源碼解讀


分享到:


相關文章: