5、spring核心源碼解析之DI依賴注入、自動裝配@Autowired和@Resource

前情

​ spirng 的DI(dependency injection )依賴注入是我們在開發中最常用的,如setter注入、構造方法注入,還有我們的自動裝配@Autowired、@Resource注入,它可以幫助我們更好的關注我們的業務開發,而無需關注DI依賴注入的實現細節。

​ 爲什麼有時候我們還可以將一個接口的多個實現類通過map的形式進行注入呢?難道你在開發的過程中對spring如何實現通過一個註解就完成這些就不好奇嗎?

爲了更清晰的理解概念,本文將採用xml和java配置類結合的方式進行講解。

1.依賴注入之setter注入

兩個實體類Person、Car

public class Person {
	private String name;
	private Car car;

//	public Person(String name, Car car) {
//		this.name = name;
//		this.car = car;
//	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public Car getCar() {
		return car;
	}

	public void setCar(Car car) {
		this.car = car;
	}
}
public class Car {
	private String name;
	private String color;
	private String price;

	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getColor() {
		return color;
	}
	public void setColor(String color) {
		this.color = color;
	}
	public String getPrice() {
		return price;
	}
	public void setPrice(String price) {
		this.price = price;
	}
}

使用xml方式完成setter注入,很簡單的setter屬性注入。

	<bean id="car" class="com.upanda.app.test.Car"></bean>
	<!-- 1.setter方法方式注入-->
	<bean id="person" class="com.upanda.app.test.Person">
		<property name="car" ref="car"/>
		<property name="name" value="coyhzx"/>
	</bean>

使用java配置類的方式完成setter注入,個人理解~就是調用setter方法注入屬性。

public void setCar(Car car) {
		this.car = car;
	}

2.依賴注入之構造方法注入

使用xml方式完成構造方法注入,很簡單的setter屬性注入。

	<bean id="car" class="com.upanda.app.test.Car"></bean>
<!--2.構造方法方式注入-->
	<bean id="person" class="com.upanda.app.test.Person">
		<constructor-arg name="name" value="coyhzx"/>
		<constructor-arg name="car" ref="car"/>
	</bean>

java配置類的方式完成構造方法注入,個人理解就是通過構造方法注入。

public Person(String name, Car car) {
		this.name = name;
		this.car = car;
	}

3.依賴注入之自動裝配之@Autowired、@Resource等註解詳解

	<!-- 3.自動裝配 Autowired -->
	<bean id="person" class="com.upanda.app.test.Person" autowire="byName" >
	</bean>

至於java配置類方式的注入,最常見的就是service中,注入dao了,此處類似。

@Autowired
private Car car;

@Autowried自動裝配的方式有三種,在Autowire枚舉類中有定義

Autowire#NO:表示沒有自動裝配的常量(注入:並不代表這種就是不能自動裝配)。

Autowire#BY_NAME:表示通過名稱自動裝配

Autowire#BY_TYPE:表示通過類型自動裝配

在AutowireCapableBeanFactory類中定義了五種依賴注入,包括上述三種的自動裝配。

AUTOWIRE_NO:表示沒有外部的自動裝配,但是BeanFactoryAware和註解驅動的注入仍舊能應用。

應用場景:在創建bean、指定autowire方式、bean的屬性注入時應用。

AUTOWIRE_BY_NAME:表示通過名稱依賴注入

應用場景:在創建bean、指定autowire方式、bean的屬性注入時應用。

AUTOWIRE_BY_TYPE:表示通過類型依賴注入

應用場景:在創建bean、指定autowire方式、bean的屬性注入時應用。

AUTOWIRE_CONSTRUCTOR:表示通過構造函數進行依賴注入

應用場景:在創建bean、指定autowire方式。

AUTOWIRE_AUTODETECT:表示通過內省bean類依賴注入

應用場景:在創建bean、指定autowire方式。

	/**
	 * Constant that indicates no externally defined autowiring. Note that
	 * BeanFactoryAware etc and annotation-driven injection will still be applied.
	 * @see #createBean 創建bean時
	 * @see #autowire 自動注入
	 * @see #autowireBeanProperties bean的屬性注入
	 */
	//指示沒有外部定義的自動裝配的常數。注意BeanFactoryAware等和註解驅動的注入將仍然適用。
	int AUTOWIRE_NO = 0;

	/**
	 * Constant that indicates autowiring bean properties by name
	 * (applying to all bean property setters).
	 * @see #createBean
	 * @see #autowire
	 * @see #autowireBeanProperties
	 */
	//通過名稱指示自動裝配Bean屬性的常數,應用於所有bean屬性設置
	int AUTOWIRE_BY_NAME = 1;

	/**
	 * Constant that indicates autowiring bean properties by type
	 * (applying to all bean property setters).
	 * @see #createBean
	 * @see #autowire
	 * @see #autowireBeanProperties
	 */
	//指示按類型自動裝配Bean屬性的常量,應用於所有bean屬性設置
	int AUTOWIRE_BY_TYPE = 2;

	/**
	 * Constant that indicates autowiring the greediest constructor that
	 * can be satisfied (involves resolving the appropriate constructor).
	 * @see #createBean
	 * @see #autowire
	 */
	//指示自動裝配可以滿足最貪婪的構造函數的常數(涉及解析適當的構造函數)
	int AUTOWIRE_CONSTRUCTOR = 3;

	/**
	 * Constant that indicates determining an appropriate autowire strategy
	 * through introspection of the bean class.
	 * @see #createBean
	 * @see #autowire
	 * @deprecated as of Spring 3.0: If you are using mixed autowiring strategies,
	 * prefer annotation-based autowiring for clearer demarcation of autowiring needs.
	 */
	//指示通過內省bean類確定適當的自動裝配策略的常數
	@Deprecated
	int AUTOWIRE_AUTODETECT = 4;

總結一下,其實依賴注入和自動裝配的概率有點模糊,總之就是我們可以通過spring ioc容器自動的完成了我們的依賴注入和自動裝配,如果非要說有什麼區別,通過我剛剛給出的兩個類裏定義的依賴方式,可以發現依賴注入的本質是包含了自動裝配。

好了,接下來我們就來具體看看源碼是怎麼完成 自動裝配的。

4.依賴注入源碼分析

  1. 首先構造方法注入流程
    在這裏插入圖片描述

  2. 分析核心源碼,最核心代碼在ConstructorResolver#resolveAutowiredArgument方法中。

在該方法中,重點就是從beanFacotry容器中解析依賴----後續是beanFactory根據類型查找Bean,這個不復雜,但是代碼量還有點大,有興趣的朋友可以繼續跟蹤一下。

最後獲取到參數之後,bean的實例化時就是通過有參構造進行反射實例化bean。

protected Object resolveAutowiredArgument(MethodParameter param, String beanName,
			@Nullable Set<String> autowiredBeanNames, TypeConverter typeConverter, boolean fallback) {

		Class<?> paramType = param.getParameterType();
		if (InjectionPoint.class.isAssignableFrom(paramType)) {
			InjectionPoint injectionPoint = currentInjectionPoint.get();
			if (injectionPoint == null) {
				throw new IllegalStateException("No current InjectionPoint available for " + param);
			}
			return injectionPoint;
		}
		try {
            //重點,從beanFacotry容器中解析依賴----後續是beanFactory根據類型查找Bean。
			return this.beanFactory.resolveDependency(
					new DependencyDescriptor(param, true), beanName, autowiredBeanNames, typeConverter);
		}
    }

​ 其他注入方式大同小異,有興趣的可以自行了解一下。

5.@Autowired和@Resource的應用與分析

@Autowired: 是spring自動裝配的註解,默認按照類型byType進行自動裝配。

@Resource:是jdk自帶的一個註解,在spring中也具有自動裝配功能,其中可以通過指定name,通過名稱進行裝配,也可以指定type,按照類型進行自動裝配。同時指定同時指定name和type,這樣只有兩者都滿足才能進行自動裝配。

@Autowired和@Resource這兩個註解都是通過後置處理器進行解析完成自動裝配,屬性填充的。

@Autowired註解是被AutowiredAnnotationBeanPostProcessor進行解析完成自動裝配的。

@Resource註解是被CommonAnnotationBeanPostProcessor進行解析完成自動裝配的。

兩者直接或間接的實現了InstantiationAwareBeanPostProcessor接口,都有一個相同的方法postProcessProperties,處理bean的自動裝配屬性。

CommonAnnotationBeanPostProcessor#postProcessProperties方法。

	public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
		InjectionMetadata metadata = findResourceMetadata(beanName, bean.getClass(), pvs);
		try {
			metadata.inject(bean, beanName, pvs);
		}
		catch (Throwable ex) {
			throw new BeanCreationException(beanName, "Injection of resource dependencies failed", ex);
		}
		return pvs;
	}

AutowiredAnnotationBeanPostProcessor#postProcessProperties方法。

	@Override
	public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
		InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
		try {
			metadata.inject(bean, beanName, pvs);
		}
		catch (BeanCreationException ex) {
			throw ex;
		}
		catch (Throwable ex) {
			throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);
		}
		return pvs;
	}

具體的依賴注入過程其實我在spring核心源碼分析的第二篇文章的依賴注入中講了,不重複敘述了。

6.ioc小結

​ 整體的ioc容器,依賴注入的思想在一次次的源碼分析之後,我並沒有覺得很複雜。但是這也只是spring的核心思想之一,也是最基礎的。只有在充分了解了bean的初始化過程,以及容器的初始化過程,在後續,我們才能慢慢的理解spring boot是爲何、如何一步步將其做成啓動依賴的自動裝配。

​ 只有自己用心思考,深入源碼,才能在日常遇到問題時,做到處變不驚,冷靜分析、解決問題。

​ 學習優秀的代碼編程設計思想,提升自我的代碼編程設計思想。

上一篇:4、spring核心源碼解析之自定義bean三種方式@import、ImportSelector、ImportBeanDefinitionRegistrar
[下一篇:6.未完待續]

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