1. 程式人生 > >這篇文章,我們來談一談Spring中的屬性注入

這篇文章,我們來談一談Spring中的屬性注入

> **本系列文章:** > > [讀原始碼,我們可以從第一行讀起](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