這篇文章,我們來談一談Spring中的屬性注入
阿新 • • 發佈:2020-06-22
> **本系列文章:**
>
> [讀原始碼,我們可以從第一行讀起](https://blog.csdn.net/qq_41907991/article/details/105667900)
>
> [你知道Spring是怎麼解析配置類的嗎?](https://blog.csdn.net/qq_41907991/article/details/105743462)
>
> [配置類為什麼要新增@Configuration註解?](https://blog.csdn.net/qq_41907991/article/details/106008651)
>
> [談談Spring中的物件跟Bean,你知道Spring怎麼建立物件的嗎?](https://blog.csdn.net/qq_41907991/article/details/106111791)
>
> **推薦閱讀:**
>
> [Spring官網閱讀 | 總結篇](https://blog.csdn.net/qq_41907991/article/details/105502255)
>
> [Spring雜談](https://blog.csdn.net/qq_41907991/category_9808373.html)
>
> 本系列文章將會帶你一行行的將Spring的原始碼吃透,推薦閱讀的文章是閱讀原始碼的基礎!
# 前言
在前面的文章中已經知道了Spring是如何將一個物件創建出來的,那麼緊接著,Spring就需要將這個物件變成一個真正的Bean了,這個過程主要分為兩步
1. 屬性注入
2. 初始化
在這兩個過程中,Bean的後置處理器會穿插執行,其中有些後置處理器是為了幫助完成屬性注入或者初始化的,而有些後置處理器是Spring提供給程式設計師進行擴充套件的,當然,這二者並不衝突。**整個Spring建立物件並將物件變成Bean的過程就是我們經常提到了Spring中Bean的生命週期**。當然,本系列原始碼分析的文章不會再對生命週期的概念做過多闡述了,如果大家有這方面的需求的話可以參考我之前的文章,或者關注我的公眾號:**程式設計師DMZ**
[Spring官網閱讀(九)Spring中Bean的生命週期(上)](https://blog.csdn.net/qq_41907991/article/details/104786530)
[Spring官網閱讀(十)Spring中Bean的生命週期(下)](https://blog.csdn.net/qq_41907991/article/details/104786584)
# 原始碼分析
閒話不再多說,我們正式進入原始碼分析階段,本文重點要分析的方法就是`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean`,其原始碼如下:
## doCreateBean
```java
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {
// 建立物件的過程在上篇文章中我們已經介紹過了,這裡不再贅述
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
// 獲取到建立的這個物件
final Object bean = instanceWrapper.getWrappedInstance();
Class> beanType = instanceWrapper.getWrappedClass();
if (beanType != NullBean.class) {
mbd.resolvedTargetType = beanType;
}
// Allow post-processors to modify the merged bean definition.
// 按照官方的註釋來說,這個地方是Spring提供的一個擴充套件點,對程式設計師而言,我們可以通過一個實現了MergedBeanDefinitionPostProcessor的後置處理器來修改bd中的屬性,從而影響到後續的Bean的生命週期
// 不過官方自己實現的後置處理器並沒有去修改bd,而是呼叫了applyMergedBeanDefinitionPostProcessors方法
// 這個方法名直譯過來就是-應用合併後的bd,也就是說它這裡只是對bd做了進一步的使用而沒有真正的修改
synchronized (mbd.postProcessingLock) {
// bd只允許被處理一次
if (!mbd.postProcessed) {
try {
// 應用合併後的bd
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
}
catch (Throwable ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Post-processing of merged bean definition failed", ex);
}
// 標註這個bd已經被MergedBeanDefinitionPostProcessor的後置處理器處理過
// 那麼在第二次建立Bean的時候,不會再次呼叫applyMergedBeanDefinitionPostProcessors
mbd.postProcessed = true;
}
}
// 這裡是用來出來迴圈依賴的,關於迴圈以來,在介紹完正常的Bean的建立後,單獨用一篇文章說明
// 這裡不做過多解釋
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isTraceEnabled()) {
logger.trace("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
Object exposedObject = bean;
try {
// 我們這篇文章重點要分析的就是populateBean方法,在這個方法中完成了屬性注入
populateBean(beanName, mbd, instanceWrapper);
// 初始化
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
catch (Throwable ex) {
// 省略異常程式碼
}
// 後續程式碼不在本文探討範圍內了,暫不考慮
return exposedObject;
}
```
## applyMergedBeanDefinitionPostProcessors
原始碼如下:
```java
// 可以看到這個方法的程式碼還是很簡單的,就是呼叫了MergedBeanDefinitionPostProcessor的postProcessMergedBeanDefinition方法
protected void applyMergedBeanDefinitionPostProcessors(RootBeanDefinition mbd, Class> beanType, String beanName) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof MergedBeanDefinitionPostProcessor) {
MergedBeanDefinitionPostProcessor bdp = (MergedBeanDefinitionPostProcessor) bp;
bdp.postProcessMergedBeanDefinition(mbd, beanType, beanName);
}
}
}
```
這個時候我們就要思考一個問題,容器中現在有哪些後置處理器是`MergedBeanDefinitionPostProcessor`呢?
![image-20200613200058693](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9naXRlZS5jb20vd3hfY2MzNDdiZTY5Ni9ibG9nSW1hZ2UvcmF3L21hc3Rlci9pbWFnZS0yMDIwMDYxMzIwMDA1ODY5My5wbmc?x-oss-process=image/format,png)
檢視這個方法的實現類我們會發現總共就這麼幾個類實現了`MergedBeanDefinitionPostProcessor`介面。實際上除了`ApplicationListenerDetector`之外,其餘的後置處理器的邏輯都差不多。我們在這裡我們主要就分析兩個後置處理
1. ApplicationListenerDetector
2. AutowiredAnnotationBeanPostProcessor
### ApplicationListenerDetector
首先,我們來`ApplicationListenerDetector`,這個類在之前的文章中也多次提到過了,它的作用是用來處理巢狀Bean的情況,主要是保證能將巢狀在Bean標籤中的`ApplicationListener`也能新增到容器的監聽器集合中去。我們先通過一個例子來感受下這個後置處理器的作用吧
配置檔案:
```xml
```
示例程式碼:
```java
// 事件
public class DmzEvent extends ApplicationEvent {
public DmzEvent(Object source) {
super(source);
}
}
public class DmzService {
OrderService orderService;
public DmzService(OrderService orderService) {
this.orderService = orderService;
}
}
// 實現ApplicationListener介面
public class OrderService implements ApplicationListener {
@Override
public void onApplicationEvent(DmzEvent event) {
System.out.println(event.getSource());
}
}
public class Main {
public static void main(String[] args) {
ClassPathXmlApplicationContext cc = new ClassPathXmlApplicationContext("application-populate.xml");
cc.publishEvent(new DmzEvent("my name is dmz"));
}
}
// 程式執行結果,控制檯列印:my name is dmz
```
說明`OrderService`已經被新增到了容器的監聽器集合中。但是請注意,在這種情況下,如果要使`OrderService`能夠執行監聽的邏輯,必須要滿足下面這兩個條件
- 外部的Bean要是單例的,對於我們的例子而言就是dmzService
- 內嵌的Bean也必須是單例的,在上面的例子中也就是orderService必須是單例
另外需要注意的是,這種巢狀的Bean比較特殊,它雖然由Spring建立,但是確不存在於容器中,就是說我們不能將其作為依賴注入到別的Bean中。
### AutowiredAnnotationBeanPostProcessor
對應原始碼如下:
```java
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class> beanType, String beanName) {
// 找到注入的元資料,第一次是構建,後續可以直接從快取中拿
// 註解元資料其實就是當前這個類中的所有需要進行注入的“點”的集合,
// 注入點(InjectedElement)包含兩種,欄位/方法
// 對應的就是AutowiredFieldElement/AutowiredMethodElement
InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null);
// 排除掉被外部管理的注入點
metadata.checkConfigMembers(beanDefinition);
}
```
上面程式碼的核心邏輯就是
- 找到所有的注入點,其實就是被@Autowired註解修飾的方法以及欄位,同時靜態的方法以及欄位也會被排除
- 排除掉被外部管理的注入點,在後續的原始碼分析中我們再細說
#### findAutowiringMetadata
```java
// 這個方法的核心邏輯就是先從快取中獲取已經解析好的注入點資訊,很明顯,在原型情況下才會使用快取
// 建立注入點的核心邏輯在buildAutowiringMetadata方法中
private InjectionMetadata findAutowiringMetadata(String beanName, Class> clazz, @Nullable PropertyValues pvs) {
String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
// 可能我們會修改bd中的class屬性,那麼InjectionMetadata中的注入點資訊也需要重新整理
if (InjectionMetadata.needsRefresh(metadata, clazz)) {
synchronized (this.injectionMetadataCache) {
metadata = this.injectionMetadataCache.get(cacheKey);
if (InjectionMetadata.needsRefresh(metadata, clazz)) {
if (metadata != null) {
metadata.clear(pvs);
}
// 這裡真正建立注入點
metadata = buildAutowiringMetadata(clazz);
this.injectionMetadataCache.put(cacheKey, metadata);
}
}
}
return metadata;
}
```
#### buildAutowiringMetadata
```java
// 我們應用中使用@Autowired註解標註在欄位上或者setter方法能夠完成屬性注入
// 就是因為這個方法將@Autowired註解標註的方法以及欄位封裝成InjectionMetadata
// 在後續階段會呼叫InjectionMetadata的inject方法進行注入
private InjectionMetadata buildAutowiringMetadata(final Class> clazz) {
List elements = new ArrayList<>();
Class> targetClass = clazz;
do {
final List currElements = new ArrayList<>();
// 處理所有的被@AutoWired/@Value註解標註的欄位
ReflectionUtils.doWithLocalFields(targetClass, field -> {
AnnotationAttributes ann = findAutowiredAnnotation(field);
if (ann != null) {
// 靜態欄位會直接跳過
if (Modifier.isStatic(field.getModifiers())) {
// 省略日誌列印
return;
}
// 得到@AutoWired註解中的required屬性
boolean required = determineRequiredStatus(ann);
currElements.add(new AutowiredFieldElement(field, required));
}
});
// 處理所有的被@AutoWired註解標註的方法,相對於欄位而言,這裡需要對橋接方法進行特殊處理
ReflectionUtils.doWithLocalMethods(targetClass, method -> {
// 只處理一種特殊的橋接場景,其餘的橋接方法都會被忽略
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
return;
}
AnnotationAttributes ann = findAutowiredAnnotation(bridgedMethod);
// 處理方法時需要注意,當父類中的方法被子類重寫時,如果子父類中的方法都加了@Autowired
// 那麼此時父類方法不能被處理,即不能被封裝成一個AutowiredMethodElement
if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
if (Modifier.isStatic(method.getModifiers())) {
// 省略日誌列印
return;
}
if (method.getParameterCount() == 0) {
// 當方法的引數數量為0時,雖然不需要進行注入,但是還是會把這個方法作為注入點使用
// 這個方法最終還是會被呼叫
if (logger.isInfoEnabled()) {
logger.info("Autowired annotation should only be used on methods with parameters: " +
method);
}
}
boolean required = determineRequiredStatus(ann);
// PropertyDescriptor: 屬性描述符
// 就是通過解析getter/setter方法,例如void getA()會解析得到一個屬性名稱為a
// readMethod為getA的PropertyDescriptor,
// 在《Spring官網閱讀(十四)Spring中的BeanWrapper及型別轉換》文中已經做過解釋
// 這裡不再贅述,這裡之所以來這麼一次查詢是因為當XML中對這個屬性進行了配置後,
// 那麼就不會進行自動注入了,XML中顯示指定的屬性優先順序高於註解
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz); // 構造一個對應的AutowiredMethodElement,後續這個方法會被執行
// 方法的引數會被自動注入,這裡不限於setter方法
currElements.add(new AutowiredMethodElement(method, required, pd));
}
});
// 會處理父類中欄位上及方法上的@AutoWired註解,並且父類的優先順序比子類高
elements.addAll(0, currElements);
targetClass = targetClass.getSuperclass();
}
while (targetClass != null && targetClass != Object.class);
return new InjectionMetadata(clazz, elements);
}
```
##### 難點程式碼分析
上面的程式碼整體來說應該很簡單,就如我們之前所說的,處理帶有@Autowired註解的欄位及方法,同時會過濾掉所有的靜態欄位及方法。上面複雜的地方在於對橋接方法的處理,可能大部分人都沒辦法理解這幾行程式碼:
```java
// 第一行
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
// 第二行
if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
return;
}
// 第三行
if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
}
```
要理解這些程式碼,首先你得知道什麼是橋接,為此我已經寫好了一篇文章:
[Spring雜談 | 從橋接方法到JVM方法呼叫](https://daimingzhi.blog.csdn.net/article/details/106631456)
除了在上面的文章中提到的橋接方法外,還有一種特殊的情況
```java
// A類跟B類在同一個包下,A不是public的
class A {
public void test(){
}
}
// 在B中會生成一個跟A中的方法描述符(引數+返回值)一模一樣的橋接方法
// 這個橋接方法實際上就是呼叫父類中的方法
// 具體可以參考:https://bugs.java.com/bugdatabase/view_bug.do?bug_id=63424113
public class B extends A {
}
```
在理解了什麼是橋接之後,那麼上邊的`第一行`程式碼你應該就能看懂了,就以上面的程式碼為例,B中會生成一個橋接方法,對應的被橋接的方法就是A中的`test`方法。
接著,我們看看第二行程式碼
```java
public static boolean isVisibilityBridgeMethodPair(Method bridgeMethod, Method bridgedMethod) {
// 說明這個方法本身就不是橋接方法,直接返回true
if (bridgeMethod == bridgedMethod) {
return true;
}
// 說明是橋接方法,並且方法描述符一致
// 當且僅當是上面例子中描述的這種橋接的時候這個判斷才會滿足
// 正常來說橋接方法跟被橋接方法的返回值+引數型別肯定不一致
// 所以這個判斷會過濾掉其餘的所有型別的橋接方法
// 只會保留本文提及這種特殊情況下產生的橋接方法
return (bridgeMethod.getReturnType().equals(bridgedMethod.getReturnType()) &&
Arrays.equals(bridgeMethod.getParameterTypes(), bridgedMethod.getParameterTypes()));
}
```
最後,再來看看`第三行`程式碼,核心就是這句` method.equals(ClassUtils.getMostSpecificMethod(method, clazz)`。這句程式碼的主要目的就是為了處理下面這種情況
```java
@Component
public class D extends C {
@Autowired
@Override
public void setDmzService(DmzService dmzService) {
dmzService.init();
this.dmzService = dmzService;
}
}
// C不是Spring中的元件
public class C {
DmzService dmzService;
@Autowired
public void setDmzService(DmzService dmzService) {
this.dmzService = dmzService;
}
}
```
這種情況下,在處理D中的@Autowired註解時,雖然我們要處理父類中的`@Autowired`註解,但是因為子類中的方法已經複寫了父類中的方法,所以此時應該要跳過父類中的這個被複寫的方法,這就是`第三行`程式碼的作用。
### 小結
到這裡我們主要分析了`applyMergedBeanDefinitionPostProcessors`這段程式碼的作用,它的執行時機是在建立物件之後,屬性注入之前。按照官方的定義來說,到這裡我們仍然可以使用這個方法來修改bd的定義,那麼相對於通過`BeanFactoryPostProcessor`的方式修改bd,`applyMergedBeanDefinitionPostProcessors`這個方法影響的範圍更小,`BeanFactoryPostProcessor`影響的是整個Bean的生命週期,而`applyMergedBeanDefinitionPostProcessors`只會影響屬性注入之後的生命週期。
其次,我們分析了Spring中內建的`MergedBeanDefinitionPostProcessor`,選取了其中兩個特殊的後置處理器進行分析,其中`ApplicationListenerDetector`主要處理內嵌的事件監聽器,而`AutowiredAnnotationBeanPostProcessor`主要用於處理@Autowired註解,實際上我們會發現,到這裡還只是完成了@Autowired註解的解析,還沒有真正開始進行注入,真正注入的邏輯在後面我們要分析的`populateBean`方法中,在這個方法中會使用解析好的注入元資訊完成真正的屬性注入,那麼接下來我們就開始分析`populateBean`這個方法的原始碼。
## populateBean
> 迴圈依賴的程式碼我們暫且跳過,後續出一篇專門文章解讀迴圈依賴,我們直接看看populateBean到底做了什麼。
```java
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
// 處理空例項
if (bw == null) {
// 如果建立的物件為空,但是在XML中又配置了需要注入的屬性的話,那麼直接報錯
if (mbd.hasPropertyValues()) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance");
}
else {
// 空物件,不進行屬性注入
return;
}
}
// 滿足兩個條件,不是合成類 && 存在InstantiationAwareBeanPostProcessor
// 其中InstantiationAwareBeanPostProcessor主要作用就是作為Bean的例項化前後的鉤子
// 外加完成屬性注入,對於三個方法就是
// postProcessBeforeInstantiation 建立物件前呼叫
// postProcessAfterInstantiation 物件建立完成,@AutoWired註解解析後呼叫
// postProcessPropertyValues(已過期,被postProcessProperties替代) 進行屬性注入
// 下面這段程式碼的主要作用就是我們可以提供一個InstantiationAwareBeanPostProcessor
// 提供的這個後置處理如果實現了postProcessAfterInstantiation方法並且返回false
// 那麼可以跳過Spring預設的屬性注入,但是這也意味著我們要自己去實現屬性注入的邏輯
// 所以一般情況下,我們也不會這麼去擴充套件
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
return;
}
}
}
}
// 這裡其實就是判斷XML是否提供了屬性相關配置
PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);
// 確認注入模型
int resolvedAutowireMode = mbd.getResolvedAutowireMode();
// 主要處理byName跟byType兩種注入模型,byConstructor這種注入模型在建立物件的時候已經處理過了
// 這裡都是對自動注入進行處理,byName跟byType兩種注入模型均是依賴setter方法
// byName,根據setter方法的名字來查詢對應的依賴,例如setA,那麼就是去容器中查詢名字為a的Bean
// byType,根據setter方法的引數型別來查詢對應的依賴,例如setXx(A a),就是去容器中查詢型別為A的bean
if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
if (resolvedAutowireMode == AUTOWIRE_BY_NAME) {
autowireByName(beanName, mbd, bw, newPvs);
}
if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
autowireByType(beanName, mbd, bw, newPvs);
}
// pvs是XML定義的屬性
// 自動注入後,bean實際用到的屬性就應該要替換成自動注入後的屬性
pvs = newPvs;
}
// 檢查是否有InstantiationAwareBeanPostProcessor
// 前面說過了,這個後置處理器就是來完成屬性注入的
boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
// 是否需要依賴檢查,預設是不會進行依賴檢查的
boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE);
// 下面這段程式碼有點麻煩了,因為涉及到版本問題
// 其核心程式碼就是呼叫了postProcessProperties完成了屬性注入
PropertyDescriptor[] filteredPds = null;
// 存在InstantiationAwareBeanPostProcessor,我們需要呼叫這類後置處理器的方法進行注入
if (hasInstAwareBpps) {
if (pvs == null) {
pvs = mbd.getPropertyValues();
}
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
// 這句就是核心
PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
if (pvsToUse == null) {
if (filteredPds == null) {
// 得到需要進行依賴檢查的屬性的集合
filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
}
// 這個方法已經過時了,放到這裡就是為了相容老版本
pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
if (pvsToUse == null) {
return;
}
}
pvs = pvsToUse;
}
}
}
// 需要進行依賴檢查
if (needsDepCheck) {
if (filteredPds == null) {
// 得到需要進行依賴檢查的屬性的集合
filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
}
// 對需要進行依賴檢查的屬性進行依賴檢查
checkDependencies(beanName, mbd, filteredPds, pvs);
}
// 將XML中的配置屬性應用到Bean上
if (pvs != null) {
applyPropertyValues(beanName, mbd, bw, pvs);
}
}
```
上面這段程式碼主要可以拆分為三個部分
1. 處理自動注入
2. 處理屬性注入(主要指處理@Autowired註解),最重要
3. 處理依賴檢查
### 處理自動注入
#### autowireByName
對應原始碼如下:
```java
protected void autowireByName(
String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {
// 得到符合下面條件的屬性名稱
// 1.有setter方法
// 2.需要進行依賴檢查
// 3.不包含在XML配置中
// 4.不是簡單型別(基本資料型別,列舉,日期等)
// 這裡可以看到XML配置優先順序高於自動注入的優先順序
// 不進行依賴檢查的屬性,也不會進行屬性注入
String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
for (String propertyName : propertyNames) {
if (containsBean(propertyName)) {
Object bean = getBean(propertyName);
// 將自動注入的屬性新增到pvs中去
pvs.add(propertyName, bean);
// 註冊bean之間的依賴關係
registerDependentBean(propertyName, beanName);
// 忽略日誌
}
// 忽略日誌
}
}
```
看到了嗎?程式碼就是這麼的簡單,不是要通過名稱注入嗎?直接通過beanName呼叫getBean,完事兒
#### autowireByType
```java
protected void autowireByType(
String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {
// 這個型別轉換器,主要是在處理@Value時需要使用
TypeConverter converter = getCustomTypeConverter();
if (converter == null) {
converter = bw;
}
Set autowiredBeanNames = new LinkedHashSet<>(4);
// 得到符合下面條件的屬性名稱
// 1.有setter方法
// 2.需要進行依賴檢查
// 3.不包含在XML配置中
// 4.不是簡單型別(基本資料型別,列舉,日期等)
// 這裡可以看到XML配置優先順序高於自動注入的優先順序
String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
for (String propertyName : propertyNames) {
try {
PropertyDescriptor pd = bw.getPropertyDescriptor(propertyName);
if (Object.class != pd.getPropertyType()) {
// 這裡獲取到的就是setter方法的引數,因為我們需要按照型別進行注入嘛
MethodParameter methodParam = BeanUtils.getWriteMethodParameter(pd);
// 如果是PriorityOrdered在進行型別匹配時不會去匹配factoryBean
// 如果不是PriorityOrdered,那麼在查詢對應型別的依賴的時候會會去匹factoryBean
// 這就是Spring的一種設計理念,實現了PriorityOrdered介面的Bean被認為是一種
// 最高優先順序的Bean,這一類的Bean在進行為了完成裝配而去檢查型別時,
// 不去檢查factoryBean
// 具體可以參考PriorityOrdered介面上的註釋文件
boolean eager = !(bw.getWrappedInstance() instanceof PriorityOrdered);
// 將引數封裝成為一個依賴描述符
// 依賴描述符會通過:依賴所在的類,欄位名/方法名,依賴的具體型別等來描述這個依賴
DependencyDescriptor desc = new AutowireByTypeDependencyDescriptor(methodParam, eager);
// 解析依賴,這裡會處理@Value註解
// 另外,通過指定的型別到容器中查詢對應的bean
Object autowiredArgument = resolveDependency(desc, beanName, autowiredBeanNames, converter);
if (autowiredArgument != null) {
// 將查找出來的依賴屬性新增到pvs中,後面會將這個pvs應用到bean上
pvs.add(propertyName, autowiredArgument);
}
// 註冊bean直接的依賴關係
for (String autowiredBeanName : autowiredBeanNames) {
registerDependentBean(autowiredBeanName, beanName);
if (logger.isDebugEnabled()) {
logger.debug("Autowiring by type from bean name '" + beanName + "' via property '" +
propertyName + "' to bean named '" + autowiredBeanName + "'");
}
}
autowiredBeanNames.clear();
}
}
catch (BeansException ex) {
throw new UnsatisfiedDependencyException(mbd.getResourceDescription(), beanName, propertyName, ex);
}
}
}
```
##### resolveDependency
> 這個方法在[Spring雜談 | 什麼是ObjectFactory?什麼是ObjectProvider?](https://blog.csdn.net/qq_41907991/article/details/105123387)已經做過分析了,本文不再贅述。
>
> 可以看到,真正做事的方法是`doResolveDependency`
```java
@Override
public Object resolveDependency(DependencyDescriptor descriptor, String requestingBeanName, Set autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
// descriptor代表當前需要注入的那個欄位,或者方法的引數,也就是注入點
// ParameterNameDiscovery用於解析方法引數名稱
descriptor.initParameterNameDiscovery(getParameterNameDiscoverer());
// 1. Optional
if (Optional.class == descriptor.getDependencyType()) {
return createOptionalDependency(descriptor, requestingBeanName);
// 2. ObjectFactory、ObjectProvider
} else if (ObjectFactory.class == descriptor.getDependencyType() ||
ObjectProvider.class == descriptor.getDependencyType()) {
return new DependencyObjectProvider(descriptor, requestingBeanName);
// 3. javax.inject.Provider
} else if (javaxInjectProviderClass == descriptor.getDependencyType()) {
return new Jsr330Factory().createDependencyProvider(descriptor, requestingBeanName);
} else {
// 4. @Lazy
Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(
descriptor, requestingBeanName);
// 5. 正常情況
if (result == null) {
result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
}
return result;
}
}
```
##### doResolveDependency
```java
public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,
@Nullable Set autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor);
try {
Object shortcut = descriptor.resolveShortcut(this);
if (shortcut != null) {
return shortcut;
}
// 依賴的具體型別
Class> type = descriptor.getDependencyType();
// 處理@Value註解,這裡得到的時候@Value中的值
Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);
if (value != null) {
if (value instanceof String) {
// 解析@Value中的佔位符
String strVal = resolveEmbeddedValue((String) value);
// 獲取到對應的bd
BeanDefinition bd = (beanName != null && containsBean(beanName) ? getMergedBeanDefinition(beanName) : null);
// 處理EL表示式
value = evaluateBeanDefinitionString(strVal, bd);
}
// 通過解析el表示式可能還需要進行型別轉換
TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
return (descriptor.getField() != null ?
converter.convertIfNecessary(value, type, descriptor.getField()) :
converter.convertIfNecessary(value, type, descriptor.getMethodParameter()));
}
// 對map,collection,陣列型別的依賴進行處理
// 最終會根據集合中的元素型別,呼叫findAutowireCandidates方法
Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);
if (multipleBeans != null) {
return multipleBeans;
}
// 根據指定型別可能會找到多個bean
// 這裡返回的既有可能是物件,也有可能是物件的型別
// 這是因為到這裡還不能明確的確定當前bean到底依賴的是哪一個bean
// 所以如果只會返回這個依賴的型別以及對應名稱,最後還需要呼叫getBean(beanName)
// 去建立這個Bean
Map matchingBeans = findAutowireCandidates(beanName, type, descriptor);
// 一個都沒找到,直接丟擲異常
if (matchingBeans.isEmpty()) {
if (isRequired(descriptor)) {
raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
}
return null;
}
String autowiredBeanName;
Object instanceCandidate;
// 通過型別找到了多個
if (matchingBeans.size() > 1) {
// 根據是否是主Bean
// 是否是最高優先順序的Bean
// 是否是名稱匹配的Bean
// 來確定具體的需要注入的Bean的名稱
// 到這裡可以知道,Spring在查詢依賴的時候遵循先型別再名稱的原則(沒有@Qualifier註解情況下)
autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
if (autowiredBeanName == null) {
// 無法推斷出具體的名稱
// 如果依賴是必須的,直接丟擲異常
// 如果依賴不是必須的,但是這個依賴型別不是集合或者陣列,那麼也丟擲異常
if (isRequired(descriptor) || !indicatesMultipleBeans(type)) {
return descriptor.resolveNotUnique(type, matchingBeans);
}
// 依賴不是必須的,但是依賴型別是集合或者陣列,那麼返回一個null
else {
return null;
}
}
instanceCandidate = matchingBeans.get(autowiredBeanName);
}
else {
// 直接找到了一個對應的Bean
Map.Entry entry = matchingBeans.entrySet().iterator().next();
autowiredBeanName = entry.getKey();
instanceCandidate = entry.getValue();
}
if (autowiredBeanNames != null) {
autowiredBeanNames.add(autowiredBeanName);
}
// 前面已經說過了,這裡可能返回的是Bean的型別,所以需要進一步呼叫getBean
if (instanceCandidate instanceof Class) {
instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this);
}
// 做一些檢查,如果依賴是必須的,查找出來的依賴是一個null,那麼報錯
// 查詢處理的依賴型別不符合,也報錯
Object result = instanceCandidate;
if (result instanceof NullBean) {
if (isRequired(descriptor)) {
raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
}
result = null;
}
if (!ClassUtils.isAssignableValue(type, result)) {
throw new BeanNotOfRequiredTypeException(autowiredBeanName, type, instanceCandidate.getClass());
}
return result;
}
finally {
ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint);
}
}
```
##### findAutowireCandidates
```java
protected Map findAutowireCandidates(
@Nullable String beanName, Class> requiredType, DependencyDescriptor descriptor) {
// 簡單來說,這裡就是到容器中查詢requiredType型別的所有bean的名稱的集合
// 這裡會根據descriptor.isEager()來決定是否要匹配factoryBean型別的Bean
// 如果isEager()為true,那麼會匹配factoryBean,反之,不會
String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
this, requiredType, true, descriptor.isEager());
Map result = new LinkedHashMap<>(candidateNames.length);
// 第一步會到resolvableDependencies這個集合中查詢是否已經存在瞭解析好的依賴
// 像我們之所以能夠直接在Bean中注入applicationContext物件
// 就是因為Spring之前就將這個物件放入了resolvableDependencies集合中
for (Class> autowiringType : this.resolvableDependencies.keySet()) {
if (autowiringType.isAssignableFrom(requiredType)) {
Object autowiringValue = this.resolvableDependencies.get(autowiringType);
// 如果resolvableDependencies放入的是一個ObjectFactory型別的依賴
// 那麼在這裡會生成一個代理物件
// 例如,我們可以在controller中直接注入request物件
// 就是因為,容器啟動時就在resolvableDependencies放入了一個鍵值對
// 其中key為:Request.class,value為:ObjectFactory
// 在實際注入時放入的是一個代理物件
autowiringValue = AutowireUtils.resolveAutowiringValue(autowiringValue, requiredType);
if (requiredType.isInstance(autowiringValue)) {
// 這裡放入的key不是Bean的名稱
// value是實際依賴的物件
result.put(ObjectUtils.identityToString(autowiringValue), autowiringValue);
break;
}
}
}
// 接下來開始對之前查找出來的型別匹配的所有BeanName進行處理
for (String candidate : candidateNames) {
// 不是自引用,什麼是自引用?
// 1.候選的Bean的名稱跟需要進行注入的Bean名稱相同,意味著,自己注入自己
// 2.或者候選的Bean對應的factoryBean的名稱跟需要注入的Bean名稱相同,
// 也就是說A依賴了B但是B的建立又需要依賴A
// 要符合注入的條件
if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, descriptor)) {
// 呼叫addCandidateEntry,加入到返回集合中,後文有對這個方法的分析
addCandidateEntry(result, candidate, descriptor, requiredType);
}
}
// 排除自引用的情況下,沒有找到一個合適的依賴
if (result.isEmpty() && !indicatesMultipleBeans(requiredType)) {
// 1.先走fallback邏輯,Spring提供的一個擴充套件吧,感覺沒什麼卵用
// 預設情況下fallback的依賴描述符就是自身
DependencyDescriptor fallbackDescriptor = descriptor.forFallbackMatch();
for (String candidate : candidateNames) {
if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, fallbackDescriptor)) {
addCandidateEntry(result, candidate, descriptor, requiredType);
}
}
// fallback還是失敗
if (result.isEmpty()) {
// 處理自引用
// 從這裡可以看出,自引用的優先順序是很低的,只有在容器中真正的只有這個Bean能作為
// 候選者的時候,才會去處理,否則自引用是被排除掉的
for (String candidate : candidateNames) {
if (isSelfReference(beanName, candidate) &&
// 不是一個集合或者
// 是一個集合,但是beanName跟candidate的factoryBeanName相同
(!(descriptor instanceof MultiElementDescriptor) || !beanName.equals(candidate)) &&
isAutowireCandidate(candidate, fallbackDescriptor)) {
addCandidateEntry(result, candidate, descriptor, requiredType);
}
}
}
}
return result;
}
// candidates:就是findAutowireCandidates方法要返回的候選集合
// candidateName:當前的這個候選Bean的名稱
// descriptor:依賴描述符
// requiredType:依賴的型別
private void addCandidateEntry(Map candidates, String candidateName,
DependencyDescriptor descriptor, Class> requiredType) {
// 如果依賴是一個集合,或者容器中已經包含這個單例了
// 那麼直接呼叫getBean方法建立或者獲取這個Bean
if (descriptor instanceof MultiElementDescriptor || containsSingleton(candidateName)) {
Object beanInstance = descriptor.resolveCandidate(candidateName, requiredType, this);
candidates.put(candidateName, (beanInstance instanceof NullBean ? null : beanInstance));
}
// 如果依賴的型別不是一個集合,這個時候還不能確定到底要使用哪個依賴,
// 所以不能將這些Bean創建出來,所以這個時候,放入candidates是Bean的名稱以及型別
else {
candidates.put(candidateName, getType(candidateName));
}
}
```
### 處理屬性注入(@Autowired)
#### postProcessProperties
```java
// 在applyMergedBeanDefinitionPostProcessors方法執行的時候,
// 已經解析過了@Autowired註解(buildAutowiringMetadata方法)
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
// 這裡獲取到的是解析過的快取好的注入元資料
InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
try {
// 直接呼叫inject方法
// 存在兩種InjectionMetadata
// 1.AutowiredFieldElement
// 2.AutowiredMethodElement
// 分別對應欄位的屬性注入以及方法的屬性注入
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;
}
```
##### 欄位的屬性注入
```java
// 最終反射呼叫filed.set方法
protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
Field field = (Field) this.member;
Object value;
if (this.cached) {
// 第一次注入的時候肯定沒有快取
// 這裡也是對原型情況的處理
value = resolvedCachedArgument(beanName, this.cachedFieldValue);
} else {
DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
desc.setContainingClass(bean.getClass());
Set autowiredBeanNames = new LinkedHashSet<>(1);
Assert.state(beanFactory != null, "No BeanFactory available");
TypeConverter typeConverter = beanFactory.getTypeConverter();
try {
// 這裡可以看到,對@Autowired註解在欄位上的處理
// 跟byType下自動注入的處理是一樣的,就是呼叫resolveDependency方法
value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
} catch (BeansException ex) {
throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex);
}
synchronized (this) {
// 沒有快取過的話,這裡需要進行快取
if (!this.cached) {
if (value != null || this.required) {
this.cachedFieldValue = desc;
// 註冊Bean之間的依賴關係
registerDependentBeans(beanName, autowiredBeanNames);
// 如果這個型別的依賴只存在一個的話,我們就能確定這個Bean的名稱
// 那麼直接將這個名稱快取到ShortcutDependencyDescriptor中
// 第二次進行注入的時候就可以直接呼叫getBean(beanName)得到這個依賴了
// 實際上正常也只有一個,多個就報錯了
// 另外這裡會過濾掉@Vlaue得到的依賴
if (autowiredBeanNames.size() == 1) {
String autowiredBeanName = autowiredBeanNames.iterator().next();
// 通過resolvableDependencies這個集合找的依賴不滿足containsBean條件
// 不會進行快取,因為快取實際還是要呼叫getBean,而resolvableDependencies
// 是沒法通過getBean獲取的
if (beanFactory.containsBean(autowiredBeanName) &&
beanFactory.isTypeMatch(autowiredBeanName, field.getType())) { // 依賴描述符封裝成ShortcutDependencyDescriptor進行快取
this.cachedFieldValue = new ShortcutDependencyDescriptor(
desc, autowiredBeanName, field.getType());
}
}
} else {
this.cachedFieldValue = null;
}
this.cached = true;
}
}
}
if (value != null) {
// 反射呼叫Field.set方法
ReflectionUtils.makeAccessible(field);
field.set(bean, value);
}
}
```
##### 方法的屬性注入
```java
// 程式碼看著很長,實際上邏輯跟欄位注入基本一樣
protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
// 判斷XML中是否配置了這個屬性,如果配置了直接跳過
// 換而言之,XML配置的屬性優先順序高於@Autowired註解
if (checkPropertySkipping(pvs)) {
return;
}
Method method = (Method) this.member;
Object[] arguments;
if (this.cached) {
arguments = resolveCachedArguments(beanName);
} else {
// 通過方法引數型別構造依賴描述符
// 邏輯基本一樣的,最終也是呼叫beanFactory.resolveDependency方法
Class>[] paramTypes = method.getParameterTypes();
arguments = new Object[paramTypes.length];
DependencyDescriptor[] descriptors = new DependencyDescriptor[paramTypes.length];
Set autowiredBeans = new LinkedHashSet<>(paramTypes.length);
Assert.state(beanFactory != null, "No BeanFactory available");
TypeConverter typeConverter = beanFactory.getTypeConverter();
// 遍歷方法的每個引數
for (int i = 0; i < arguments.length; i++) {
MethodParameter methodParam = new MethodParameter(method, i);
DependencyDescriptor currDesc = new DependencyDescriptor(methodParam, this.required);
currDesc.setContainingClass(bean.getClass());
descriptors[i] = currDesc;
try {
// 還是要呼叫這個方法
Object arg = beanFactory.resolveDependency(currDesc, beanName, autowiredBeans, typeConverter);
if (arg == null && !this.required) {
arguments = null;
break;
}
arguments[i] = arg;
} catch (BeansException ex) {
throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(methodParam), ex);
}
}
synchronized (this) {
if (!this.cached) {
if (arguments != null) {
Object[] cachedMethodArguments = new Object[paramTypes.length];
System.arraycopy(descriptors, 0, cachedMethodArguments, 0, arguments.length);
// 註冊bean之間的依賴關係
registerDependentBeans(beanName, autowiredBeans);
// 跟欄位注入差不多,存在@Value註解,不進行快取
if (autowiredBeans.size() == paramTypes.length) {
Iterator it = autowiredBeans.iterator();
for (int i = 0; i < paramTypes.length; i++) {
String autowiredBeanName = it.next();
if (beanFactory.containsBean(autowiredBeanName) &&
beanFactory.isTypeMatch(autowiredBeanName, paramTypes[i])) {
cachedMethodArguments[i] = new ShortcutDependencyDescriptor(
descriptors[i], autowiredBeanName, paramTypes[i]);
}
}
}
this.cachedMethodArguments = cachedMethodArguments;
} else {
this.cachedMethodArguments = null;
}
this.cached = true;
}
}
}
if (arguments != null) {
try {
// 反射呼叫方法
// 像我們的setter方法就是在這裡呼叫的
ReflectionUtils.makeAccessible(method);
method.invoke(bean, arguments);
} catch (InvocationTargetException ex) {
throw ex.getTargetException();
}
}
}
```
### 處理依賴檢查
```java
protected void checkDependencies(
String beanName, AbstractBeanDefinition mbd, PropertyDescriptor[] pds, PropertyValues pvs)
throws UnsatisfiedDependencyException {
int dependencyCheck = mbd.getDependencyCheck();
for (PropertyDescriptor pd : pds) {
// 有set方法但是在pvs中沒有對應屬性,那麼需要判斷這個屬性是否要進行依賴檢查
// 如果需要進行依賴檢查的話,就需要報錯了
// pvs中儲存的是自動注入以及XML配置的屬性
if (pd.getWriteMethod() != null && !pvs.contains(pd.getName())) {
// 是否是基本屬性,列舉/日期等也包括在內
boolean isSimple = BeanUtils.isSimpleProperty(pd.getPropertyType());
// 如果DEPENDENCY_CHECK_ALL,對任意屬性都開啟了依賴檢查,報錯
// DEPENDENCY_CHECK_SIMPLE,對基本屬性開啟了依賴檢查並且是基本屬性,報錯
// DEPENDENCY_CHECK_OBJECTS,對非基本屬性開啟了依賴檢查並且不是非基本屬性,報錯
boolean unsatisfied = (dependencyCheck == AbstractBeanDefinition.DEPENDENCY_CHECK_ALL) ||
(isSimple && dependencyCheck == AbstractBeanDefinition.DEPENDENCY_CHECK_SIMPLE) ||
(!isSimple && dependencyCheck == AbstractBeanDefinition.DEPENDENCY_CHECK_OBJECTS);
if (unsatisfied) {
throw new UnsatisfiedDependencyException(mbd.getResourceDescription(), beanName, pd.getName(),
"Set this property value or disable dependency checking for this bean.");
}
}
}
}
```
### 將解析出來的屬性應用到Bean上
到這一步解析出來的屬性主要有三個來源
1. XML中配置的
2. 通過byName的方式自動注入的
3. 通過byType的方式自動注入的
但是在應用到Bean前還需要做一步型別轉換,這一部分程式碼實際上跟我們之前在[Spring官網閱讀(十四)Spring中的BeanWrapper及型別轉換](https://blog.csdn.net/qq_41907991/article/details/105214244)介紹的差不多,而且因為XML跟自動注入的方式都不常見,正常`@Autowired`的方式進行注入的話,這個方法沒有什麼用,所以本文就不再贅述。
# 總結
本文我們主要分析了Spring在屬性注入過程中的相關程式碼,整個屬性注入可以分為兩個部分
1. `@Autowired`/`@Vale`的方式完成屬性注入
2. 自動注入(`byType`/`byName`)
完成屬性注入的核心方法其實就是`doResolveDependency`。`doResolveDependency`這個方法的邏輯簡單來說分為兩步:
1. 通過依賴型別查詢到所有的型別匹配的bean的名稱
2. 如果找到了多個的話,再根據依賴的名稱匹配對應的Bean的名稱
3. 呼叫getBean得到這個需要被注入的Bean
4. 最後反射呼叫欄位的set方法完成屬性注入
從上面也可以知道,其實整個屬性注入的邏輯是很簡單的。
如果本文對你有幫助的話,記得點個贊吧!也歡迎關注我的公眾號,微信搜尋:程式設計師DMZ,或者掃描下方二維碼,跟著我一起認認真真學Java,踏踏實實做一個coder。
![公眾號](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9naXRlZS5jb20vd3hfY2MzNDdiZTY5Ni9ibG9nSW1hZ2UvcmF3L21hc3Rlci8lRTUlODUlQUMlRTQlQkMlOTclRTUlOEYlQjcuanBn?x-oss-process=image/form