文章目錄
前情
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.依賴注入源碼分析
-
首先構造方法注入流程
-
分析核心源碼,最核心代碼在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.未完待續]