Spring 源碼導讀

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

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

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

我把 Spring Ioc 的對象轉換分爲如下 4 個步驟:app

Resource -> BeanDefinition -> BeanWrapper -> Object框架

1 Resource

Resouce 實際上是一個接口,表明的是資源,在計算機裏面從一個地方移動到另一個地方所須要的東西就是數據流,因此 Resource 實現了 InputStreamSource 接口,經過 InputStreamSource 接口能夠獲取到 Inputstream,這樣就能夠讀取不一樣的 Bean 定義了。svg

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> 標籤。
  • Properties/yml:經過 @EnableConfigurationProperties 與 @ConfigurationProperties 來定義 bean。這種形式在 Spring boot 自動注入裏面大量使用。

二、BeanDefinition

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

public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {

	ConstructorArgumentValues getConstructorArgumentValues();

	MutablePropertyValues getPropertyValues();
	
	...
}

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

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

因此 Spring 支持構造器注入與 setter 依賴注入。this

一、構造器注入

<bean id="exampleBean" class="examples.ExampleBean">
    <constructor-arg type="int" value="7500000"/>
    <constructor-arg type="java.lang.String" value="42"/>
</bean>

二、setter注入

<bean id="exampleBean" class="examples.ExampleBean">
    <!-- setter injection using the nested ref element -->
    <property name="beanOne">
        <ref bean="anotherExampleBean"/>
    </property>

    <!-- setter injection using the neater ref attribute -->
    <property name="beanTwo" ref="yetAnotherBean"/>
    <property name="integerProperty" value="1"/>
</bean>

<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>

三、BeanWapper

其實什麼是依賴注入,簡單來講就是 Spring 幫咱們建立對象。把建立對象寫死在 Java 文件變成了經過不一樣的 Spring 配置能夠注入不一樣的值。建立對象的職責由 Java 文件變成了 Spring 配置文件。

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

一、無參構造器

Object obj = new Object();
obj.setXxx(xxx);

二、有參構造器

Object obj = new Object(xxx, yyyy);
obj.setXxx(xxx);

其實 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

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

public interface TypeConverter {

	<T> T convertIfNecessary(Object value, Class<T> requiredType) throws TypeMismatchException;

	<T> T convertIfNecessary(Object value, Class<T> requiredType, MethodParameter methodParam)
			throws TypeMismatchException;

	<T> T convertIfNecessary(Object value, Class<T> requiredType, Field field)
			throws TypeMismatchException;

}

它的做用就是自動類型轉換,由於 Spring 做得太無感知了。你也許尚未感受到它的存在。不要緊,我提示一下你應該就會明白。好比,聲明一個用戶對象這個對象既有 String 類型的名稱,又有 Int 類型的年齡。 Spring 怎麼知道屬性的類型呢?這個就是 Spring 的自動類型轉換。關於 Spring 的自動類型轉換 我在以前就已經分析過了。

3.2 PropertyEditorRegistry

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

public interface PropertyEditorRegistry {

	void registerCustomEditor(Class<?> requiredType, PropertyEditor propertyEditor);

	void registerCustomEditor(Class<?> requiredType, String propertyPath, PropertyEditor propertyEditor);

	PropertyEditor findCustomEditor(Class<?> requiredType, String propertyPath);

}

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

這裏寫圖片描述

3.3 PropertyAccessor

public interface PropertyAccessor {

	boolean isReadableProperty(String propertyName);

	boolean isWritableProperty(String propertyName);
cessor method failed

	Class<?> getPropertyType(String propertyName) throws BeansException;

	TypeDescriptor getPropertyTypeDescriptor(String propertyName) throws BeansException;

	Object getPropertyValue(String propertyName) throws BeansException;

	void setPropertyValue(String propertyName, Object value) throws BeansException;

	void setPropertyValue(PropertyValue pv) throws BeansException;

	void setPropertyValues(Map<?, ?> map) throws BeansException;

	void setPropertyValues(PropertyValues pvs) throws BeansException;

	void setPropertyValues(PropertyValues pvs, boolean ignoreUnknown)
			throws BeansException;

	void setPropertyValues(PropertyValues pvs, boolean ignoreUnknown, boolean ignoreInvalid)
			throws BeansException;

}

PropertyAccessor 這個接口是判斷對象中的某個屬性是否可讀/可寫,而且能夠定入或者讀取某個屬性的值。從這個接口定義咱們能夠看出,它的使用其實就是真正用來依賴注入的。而後調用屬性操做的寫入操做,徹底依賴注入。

3.4 ConfigurablePropertyAccessor

public interface ConfigurablePropertyAccessor extends PropertyAccessor, PropertyEditorRegistry, TypeConverter {

	void setConversionService(ConversionService conversionService);

	ConversionService getConversionService();

	...
	
}

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

public class DefaultConversionService extends GenericConversionService {

	/** Java 8's java.util.Optional class available? */
	private static final boolean javaUtilOptionalClassAvailable =
			ClassUtils.isPresent("java.util.Optional", DefaultConversionService.class.getClassLoader());

	/** Java 8's java.time package available? */
	private static final boolean jsr310Available =
			ClassUtils.isPresent("java.time.ZoneId", DefaultConversionService.class.getClassLoader());

	/** Java 8's java.util.stream.Stream class available? */
	private static final boolean streamAvailable = ClassUtils.isPresent(
			"java.util.stream.Stream", DefaultConversionService.class.getClassLoader());

	public DefaultConversionService() {
		addDefaultConverters(this);
	}

	public static void addDefaultConverters(ConverterRegistry converterRegistry) {
		addScalarConverters(converterRegistry);
		addCollectionConverters(converterRegistry);

		converterRegistry.addConverter(new ByteBufferConverter((ConversionService) converterRegistry));
		if (jsr310Available) {
			Jsr310ConverterRegistrar.registerJsr310Converters(converterRegistry);
		}

		converterRegistry.addConverter(new ObjectToObjectConverter());
		converterRegistry.addConverter(new IdToEntityConverter((ConversionService) converterRegistry));
		converterRegistry.addConverter(new FallbackObjectToStringConverter());
		if (javaUtilOptionalClassAvailable) {
			converterRegistry.addConverter(new ObjectToOptionalConverter((ConversionService) converterRegistry));
		}
	}

	public static void addCollectionConverters(ConverterRegistry converterRegistry) {
		ConversionService conversionService = (ConversionService) converterRegistry;

		converterRegistry.addConverter(new ArrayToCollectionConverter(conversionService));
		converterRegistry.addConverter(new CollectionToArrayConverter(conversionService));

		converterRegistry.addConverter(new ArrayToArrayConverter(conversionService));
		converterRegistry.addConverter(new CollectionToCollectionConverter(conversionService));
		converterRegistry.addConverter(new MapToMapConverter(conversionService));

		converterRegistry.addConverter(new ArrayToStringConverter(conversionService));
		converterRegistry.addConverter(new StringToArrayConverter(conversionService));

		converterRegistry.addConverter(new ArrayToObjectConverter(conversionService));
		converterRegistry.addConverter(new ObjectToArrayConverter(conversionService));

		converterRegistry.addConverter(new CollectionToStringConverter(conversionService));
		converterRegistry.addConverter(new StringToCollectionConverter(conversionService));

		converterRegistry.addConverter(new CollectionToObjectConverter(conversionService));
		converterRegistry.addConverter(new ObjectToCollectionConverter(conversionService));

		if (streamAvailable) {
			converterRegistry.addConverter(new StreamConverter(conversionService));
		}
	}

	private static void addScalarConverters(ConverterRegistry converterRegistry) {
		converterRegistry.addConverterFactory(new NumberToNumberConverterFactory());

		converterRegistry.addConverterFactory(new StringToNumberConverterFactory());
		converterRegistry.addConverter(Number.class, String.class, new ObjectToStringConverter());

		converterRegistry.addConverter(new StringToCharacterConverter());
		converterRegistry.addConverter(Character.class, String.class, new ObjectToStringConverter());

		converterRegistry.addConverter(new NumberToCharacterConverter());
		converterRegistry.addConverterFactory(new CharacterToNumberFactory());

		converterRegistry.addConverter(new StringToBooleanConverter());
		converterRegistry.addConverter(Boolean.class, String.class, new ObjectToStringConverter());

		converterRegistry.addConverterFactory(new StringToEnumConverterFactory());
		converterRegistry.addConverter(Enum.class, String.class,
				new EnumToStringConverter((ConversionService) converterRegistry));

		converterRegistry.addConverter(new StringToLocaleConverter());
		converterRegistry.addConverter(Locale.class, String.class, new ObjectToStringConverter());

		converterRegistry.addConverter(new StringToCharsetConverter());
		converterRegistry.addConverter(Charset.class, String.class, new ObjectToStringConverter());

		converterRegistry.addConverter(new StringToCurrencyConverter());
		converterRegistry.addConverter(Currency.class, String.class, new ObjectToStringConverter());

		converterRegistry.addConverter(new StringToPropertiesConverter());
		converterRegistry.addConverter(new PropertiesToStringConverter());

		converterRegistry.addConverter(new StringToUUIDConverter());
		converterRegistry.addConverter(UUID.class, String.class, new ObjectToStringConverter());
	}

	private static final class Jsr310ConverterRegistrar {

		public static void registerJsr310Converters(ConverterRegistry converterRegistry) {
			converterRegistry.addConverter(new StringToTimeZoneConverter());
			converterRegistry.addConverter(new ZoneIdToTimeZoneConverter());
			converterRegistry.addConverter(new ZonedDateTimeToCalendarConverter());
		}
	}

}

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

3.5 BeanWrapper

public interface BeanWrapper extends ConfigurablePropertyAccessor {

	Object getWrappedInstance();

	Class<?> getWrappedClass();

	PropertyDescriptor[] getPropertyDescriptors();

	PropertyDescriptor getPropertyDescriptor(String propertyName) throws InvalidPropertyException;

	...

}

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

writeMethod.invoke(getWrappedInstance(), value);

四、總結

讓咱們再來看一下 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 IOC 有一個宏觀上面的認識。這樣就不會在複雜源碼的邏輯中迷失。