死磕Spring之IoC篇 - Bean 的屬性填充階段

月圓吖發表於2021-03-05

該系列文章是本人在學習 Spring 的過程中總結下來的,裡面涉及到相關原始碼,可能對讀者不太友好,請結合我的原始碼註釋 Spring 原始碼分析 GitHub 地址 進行閱讀

Spring 版本:5.1.14.RELEASE

開始閱讀這一系列文章之前,建議先檢視《深入瞭解 Spring IoC(面試題)》這一篇文章

該系列其他文章請檢視:《死磕 Spring 之 IoC 篇 - 文章導讀》

Bean 的屬性填充階段

當我們顯示或者隱式地呼叫AbstractBeanFactorygetBean(...) 方法時,會觸發 Bean 的載入,在《開啟 Bean 的載入》文章中分析了整個載入過程。

對於不同作用域的 Bean,底層都會呼叫 AbstractAutowireCapableBeanFactorycreateBean(...) 方法進行建立,在《Bean 的建立過程》文章中分析了整個建立過程。建立 Bean 的過程中,在獲取到的一個例項物件後,裡面的相關屬性也許是空的,那麼接下來要做的就是將需要填充的屬性進行依賴注入,然後再進行後續的初始化工作。整個的屬性填充過程非常複雜,因為配置的屬性值可能是一個表示式,需要解析,型別也可能不對,需要進行型別轉換,還可能是一個物件,需要找到對應的 Bean 然後注入(依賴注入),存在有各種處理,本文將會分析建立 Bean 過程中的屬性填充階段。

回顧

先來回顧一下建立 Bean 過程中屬性填充階段對應的程式碼:

// AbstractAutowireCapableBeanFactory#doCreateBean(...) 方法
// Initialize the bean instance.
// 開始初始化 `bean`
Object exposedObject = bean;
try {
    // <4> 對 `bean` 進行屬性填充,注入對應的屬性值
    populateBean(beanName, mbd, instanceWrapper);
    // <5> 初始化這個 `exposedObject`,呼叫其初始化方法
    exposedObject = initializeBean(beanName, exposedObject, mbd);
}
catch (Throwable ex) {
    if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
        throw (BeanCreationException) ex;
    }
    else {
        throw new BeanCreationException(
                mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
    }
}

在建立好例項物件後,這個物件的屬性還沒有賦值,所以將這個例項物件的相關屬性進行賦值,也就是上面的第 <4>

開啟 Bean 的屬性填充

populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) 方法,屬性填充,如下:

// AbstractAutowireCapableBeanFactory.java
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
    // <1> 如果例項物件為空,則進行下面的判斷
    if (bw == null) {
        // <1.1> 這個 Bean 有屬性,則丟擲異常
        if (mbd.hasPropertyValues()) {
            throw new BeanCreationException(
                    mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance");
        }
        // <1.2> 否則,不用屬性填充,直接 `return`
        else {
            // Skip property population phase for null instance.
            return;
        }
    }

    // Give any InstantiationAwareBeanPostProcessors the opportunity to modify the
    // state of the bean before properties are set. This can be used, for example,
    // to support styles of field injection.
    // <2> 例項化階段的後置處理,如果滿足這兩個條件
    if (!mbd.isSynthetic() // RootBeanDefinition 不是使用者定義的(由 Spring 解析出來的)
            && hasInstantiationAwareBeanPostProcessors()) { // 是否有 InstantiationAwareBeanPostProcessor 處理器
        // <2.1> 遍歷所有的 BeanPostProcessor
        for (BeanPostProcessor bp : getBeanPostProcessors()) {
            // 如果為 InstantiationAwareBeanPostProcessor 型別
            if (bp instanceof InstantiationAwareBeanPostProcessor) {
                InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
                // <2.2> 對例項化物件進行後置處理
                // 注意如果返回 false,直接 `return`,不會呼叫後面的 InstantiationAwareBeanPostProcessor 處理器,也不會進行接下來的屬性填充
                if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
                    return;
                }
            }
        }
    }

    // <3> 獲取 `pvs`,承載當前物件的屬性值
    PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);

    // <4> 獲取這個 Bean 的注入模式,預設為 **AUTOWIRE_NO**,例如可以通過 `@Bean` 註解的 `autowire` 屬性配置注入模式
    int resolvedAutowireMode = mbd.getResolvedAutowireMode();
    // <4.1> 如果注入模式為 **AUTOWIRE_BY_NAME** 或者 **AUTOWIRE_BY_TYPE**,則通過下面的方式獲取屬性值
    if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
        // <4.2> 將 `pvs` 封裝成 MutablePropertyValues 物件 `newPvs`(允許對屬性進行相關操作)
        MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
        // Add property values based on autowire by name if applicable.
        // <4.3> **AUTOWIRE_BY_NAME** 模式,通過名稱獲取相關屬性值,儲存在 `newPvs` 中
        if (resolvedAutowireMode == AUTOWIRE_BY_NAME) {
            autowireByName(beanName, mbd, bw, newPvs);
        }
        // Add property values based on autowire by type if applicable.
        // <4.4> **AUTOWIRE_BY_TYPE** 模式,通過型別獲取相關屬性值,儲存在 `newPvs` 中
        if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
            autowireByType(beanName, mbd, bw, newPvs);
        }
        // <4.5> 將 `newPvs` 複製給 `pvs`
        pvs = newPvs;
    }

    // 是否有 InstantiationAwareBeanPostProcessor 處理器
    boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
    // 是否需要進行依賴檢查,預設為 true
    boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE);

    PropertyDescriptor[] filteredPds = null;
    // <5> 通過 InstantiationAwareBeanPostProcessor 處理器(如果有)對 `pvs` 進行處理
    if (hasInstAwareBpps) {
        if (pvs == null) {
            pvs = mbd.getPropertyValues();
        }

        // <5.1> 遍歷所有的 BeanPostProcessor
        for (BeanPostProcessor bp : getBeanPostProcessors()) {
            // 如果為 InstantiationAwareBeanPostProcessor 型別
            if (bp instanceof InstantiationAwareBeanPostProcessor) {
                InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
                /**
                 * Spring 內部的 InstantiationAwareBeanPostProcessor 處理器:
                 * {@link AutowiredAnnotationBeanPostProcessor#postProcessProperties} 會解析 @Autowired 和 @Value 註解標註的屬性,獲取對應屬性值;
                 * {@link org.springframework.context.annotation.CommonAnnotationBeanPostProcessor#postProcessProperties} 會解析 @Resource 註解標註的屬性,獲取對應的屬性值
                 */
                // <5.2> 呼叫處理器的 `postProcessProperties(...)` 方法,對 `pvs` 進行後置處理
                PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
                // <5.3> 如果上一步的處理結果為空,可能是新版本導致的(Spring 5.1 之前沒有上面這個方法),則需要相容老版本
                if (pvsToUse == null) {
                    // <5.3.1> 找到這個 Bean 的所有 `java.beans.PropertyDescriptor` 屬性描述器(包含這個屬性的所有資訊)
                    if (filteredPds == null) {
                        filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
                    }
                    // <5.3.2> 呼叫處理器的 `postProcessPropertyValues(...)` 方法,對 `pvs` 進行後置處理
                    pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
                    // <5.3.3> 如果處理後的 PropertyValues 物件為空,直接 `return`,則不會呼叫後面的 InstantiationAwareBeanPostProcessor 處理器,也不會進行接下來的屬性填充
                    if (pvsToUse == null) {
                        return;
                    }
                }
                // <5.4> 將處理後的 `pvsToUse` 複製給 `pvs`
                pvs = pvsToUse;
            }
        }
    }
    // <6> 依賴檢查
    if (needsDepCheck) {
        // <6.1> 找到這個 Bean 的所有 `java.beans.PropertyDescriptor` 屬性描述器(包含這個屬性的所有資訊)
        if (filteredPds == null) {
            filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
        }
        // <6.2> 依賴檢查,如果沒有找到對應的屬性值,則根據檢查策略進行丟擲異常(預設不會)
        checkDependencies(beanName, mbd, filteredPds, pvs);
    }

    // <7> 如果 `pvs` 不為空,則將裡面的屬性值設定到當前 Bean 對應的屬性中(依賴注入)
    // 前面找到的屬性值並沒有設定到 Bean 中,且屬性值可能是一個表示式,型別也可能也不對,需要先進行處理和型別轉換,然後再設定到該例項物件中
    if (pvs != null) {
        applyPropertyValues(beanName, mbd, bw, pvs);
    }
}

過程大致如下:

  1. 如果例項物件為null,則進行下面的判斷

    1. 這個 Bean 有屬性,則丟擲異常
    2. 否則,不用屬性填充,直接 return
  2. 例項化階段的後置處理,如果滿足這兩個條件:RootBeanDefinition 不是使用者定義的(由 Spring 解析出來的)、是否有 InstantiationAwareBeanPostProcessor 處理器

    1. 遍歷所有的 BeanPostProcessor

    2. 如果為 InstantiationAwareBeanPostProcessor 型別,則對例項化物件進行後置處理

      注意,如果返回 false,直接 return,不會呼叫後面的 InstantiationAwareBeanPostProcessor 處理器,也不會進行接下來的屬性填充

  3. 獲取 pvs,承載當前物件的屬性值

  4. 獲取這個 Bean 的注入模式,預設為 AUTOWIRE_NO,例如可以通過 @Bean 註解的 autowire 屬性配置注入模式

    1. 如果注入模式為 AUTOWIRE_BY_NAME 或者 AUTOWIRE_BY_TYPE,則通過下面的方式獲取屬性值
    2. pvs 封裝成 MutablePropertyValues 物件 newPvs(允許對屬性進行相關操作)
    3. AUTOWIRE_BY_NAME 模式,通過名稱獲取相關屬性值,儲存在 newPvs 中,呼叫 autowireByName(...) 方法
    4. AUTOWIRE_BY_TYPE 模式,通過型別獲取相關屬性值,儲存在 newPvs 中,呼叫 autowireByType(...) 方法
    5. newPvs 複製給 pvs
  5. 通過 InstantiationAwareBeanPostProcessor 處理器(如果有)對 pvs 進行處理

    1. 遍歷所有的 BeanPostProcessor
    2. 如果為 InstantiationAwareBeanPostProcessor 型別,則呼叫其 postProcessProperties(...) 方法,對 pvs 進行後置處理
    3. 如果上一步的處理結果為空,可能是新版本導致的(Spring 5.1 之前沒有上面這個方法),則需要相容老版本
      1. 嘗試找到這個 Bean 的所有 java.beans.PropertyDescriptor 屬性描述器(包含這個屬性的所有資訊)
      2. 呼叫處理器的 postProcessPropertyValues(...) 方法,對 pvs 進行後置處理
      3. 如果處理後的 PropertyValues 物件為空,直接 return,則不會呼叫後面的處理器,也不會進行接下來的屬性填充
    4. 將處理後的 pvsToUse 複製給 pvs
  6. 依賴檢查

    1. 找到這個 Bean 的所有 java.beans.PropertyDescriptor 屬性描述器(包含這個屬性的所有資訊)
    2. 進行依賴檢查,如果沒有找到對應的屬性值,則根據檢查策略進行丟擲異常(預設不會)
  7. 如果 pvs 不為空,則將裡面的屬性值設定到當前 Bean 對應的屬性中(依賴注入),呼叫 applyPropertyValues(...) 方法

    前面找到的屬性值並沒有設定到 Bean 中,且屬性值可能是一個表示式,型別也可能也不對,需要先進行處理和型別轉換,然後再設定到該例項物件中


整個的屬性填充過程非常的複雜,接下來進行概括:

  • 允許你對例項化物件進行後置處理,處理結果為 false 表示不需要進行接下來的屬性填充過程
  • 根據注入模式,找到沒有配置屬性值的物件屬性,然後找到對應的 Bean,預設注入模式為不注入
  • 允許你對屬性值進行後置處理,例如 @Autowired@Value 等註解標註的屬性會通過這裡找到對應的屬性值(或物件)
  • 上述過程僅找到了屬性值,還沒設定到當前例項物件中,所以最後一步才是真正的屬性填充

上面有兩種注入模式:AUTOWIRE_BY_NAMEAUTOWIRE_BY_TYPE,預設為 AUTOWIRE_NO,接下來先來看看這兩種注入模式的實現

通過名稱獲取屬性值

autowireByName(String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) 方法,通過名稱獲取相關屬性值,如下:

// AbstractAutowireCapableBeanFactory.java
protected void autowireByName(String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {

    // <1> 獲取當前 Bean 中不滿意的非簡單型別的屬性名稱,也就是沒有定義屬性值的"物件"屬性
    String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
    // <2> 遍歷這些物件屬性的名稱
    for (String propertyName : propertyNames) {
        // <3> 如果當前容器存在對應的 Bean(通過名稱判斷)
        if (containsBean(propertyName)) {
            // <3.1> 根據屬性名稱獲取對應的 `bean` 物件(依賴查詢)
            Object bean = getBean(propertyName);
            // <3.2> 將 `bean` 新增至 `pvs`
            pvs.add(propertyName, bean);
            // <3.3> 將兩個 Bean 之間的依賴關係儲存起來
            registerDependentBean(propertyName, beanName);
            if (logger.isTraceEnabled()) {
                logger.trace("Added autowiring by name from bean name '" + beanName +
                        "' via property '" + propertyName + "' to bean named '" + propertyName + "'");
            }
        } else {
            if (logger.isTraceEnabled()) {
                logger.trace("Not autowiring property '" + propertyName + "' of bean '" + beanName +
                        "' by name: no matching bean found");
            }
        }
    }
}

過程並不複雜,大致如下:

  1. 獲取當前 Bean 中不滿意的非簡單型別的屬性名稱,也就是沒有定義屬性值的"物件"屬性,如下:

    // AbstractAutowireCapableBeanFactory.java
    protected String[] unsatisfiedNonSimpleProperties(AbstractBeanDefinition mbd, BeanWrapper bw) {
        Set<String> result = new TreeSet<>();
        // 獲取已設定的屬性值
        PropertyValues pvs = mbd.getPropertyValues();
        // 找到這個 Bean 的所有 PropertyDescriptor 屬性描述器(包含這個屬性的所有資訊)
        PropertyDescriptor[] pds = bw.getPropertyDescriptors();
        // 遍歷所有屬性
        for (PropertyDescriptor pd : pds) {
            if (pd.getWriteMethod() != null // 有可寫方法
                    && !isExcludedFromDependencyCheck(pd) // 不忽略
                    && !pvs.contains(pd.getName()) // 沒有對應的屬性值
                    && !BeanUtils.isSimpleProperty(pd.getPropertyType())) // 不是簡單型別(例如一個實體類)
            {
                result.add(pd.getName());
            }
        }
        // 返回這些不滿意的非簡單型別的屬性
        return StringUtils.toStringArray(result);
    }
    
  2. 遍歷這些物件屬性的名稱

  3. 如果當前容器存在對應的 Bean(通過名稱判斷)

    1. 根據屬性名稱獲取對應的 bean 物件(依賴查詢
    2. bean 新增至 pvs
    3. 將兩個 Bean 之間的依賴關係儲存起來

直接根據"物件"名稱通過 getBean(String beanName) 獲取到對應的物件(依賴查詢

通過型別獲取屬性值

autowireByType(String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) 方法,通過型別獲取相關屬性值,如下:

// AbstractAutowireCapableBeanFactory.java
protected void autowireByType(String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {

    // <1> 獲取 TypeConverter 型別轉換器,用於取代預設的 PropertyEditor 型別轉換器
    // 例如 Spring 3.0 之後的 ConversionService
    TypeConverter converter = getCustomTypeConverter();
    if (converter == null) {
        converter = bw;
    }

    Set<String> autowiredBeanNames = new LinkedHashSet<>(4);
    // <2> 獲取當前 Bean 中不滿意的非簡單型別的屬性名稱,也就是沒有定義屬性值的"物件"屬性
    String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
    // <3> 遍歷這些物件屬性的名稱
    for (String propertyName : propertyNames) {
        try {
            // <3> 獲取這個屬性的 `java.beans.PropertyDescriptor` 屬性描述器(包含這個屬性的所有資訊)
            PropertyDescriptor pd = bw.getPropertyDescriptor(propertyName);
            // <4> 如果不是 Object 型別(對 Object 類型別的 Bean 進行自動裝配毫無意義),則嘗試找到對應的物件
            if (Object.class != pd.getPropertyType()) {
                // <5> 找到這個屬性的寫方法
                MethodParameter methodParam = BeanUtils.getWriteMethodParameter(pd);
                // Do not allow eager init for type matching in case of a prioritized post-processor.
                // 是否可以提前初始化
                boolean eager = !(bw.getWrappedInstance() instanceof PriorityOrdered);
                // <6> 建立對應的依賴注入描述物件
                DependencyDescriptor desc = new AutowireByTypeDependencyDescriptor(methodParam, eager);
                // <7> 依賴注入,找到該屬性對應的物件
                Object autowiredArgument = resolveDependency(desc, beanName, autowiredBeanNames, converter);
                // <8> 如果找到屬性物件,則將該其新增至 `pvs`
                if (autowiredArgument != null) {
                    pvs.add(propertyName, autowiredArgument);
                }
                // <9> 將注入的屬性物件和當前 Bean 之前的關係儲存起來
                // 因為該屬性可能是一個集合,找到了多個物件,所以這裡是一個陣列
                for (String autowiredBeanName : autowiredBeanNames) {
                    // 將 `autowiredBeanName` 與 `beanName` 的依賴關係儲存
                    registerDependentBean(autowiredBeanName, beanName);
                    if (logger.isTraceEnabled()) {
                        logger.trace("Autowiring by type from bean name '" + beanName + "' via property '" +
                                propertyName + "' to bean named '" + autowiredBeanName + "'");
                    }
                }
                // 清空 `autowiredBeanName` 陣列
                autowiredBeanNames.clear();
            }
        } catch (BeansException ex) {
            throw new UnsatisfiedDependencyException(mbd.getResourceDescription(), beanName, propertyName, ex);
        }
    }
}

過程大致如下:

  1. 獲取 TypeConverter 型別轉換器,用於取代預設的 PropertyEditor 型別轉換器
  2. 獲取當前 Bean 中不滿意的非簡單型別的屬性名稱,也就是沒有定義屬性值的"物件"屬性,和通過名稱注入的過程一樣
  3. 遍歷這些"物件"屬性的名稱,獲取這個屬性的 java.beans.PropertyDescriptor 屬性描述器(包含這個屬性的所有資訊)
  4. 如果不是 Object 型別(對 Object 類型別的 Bean 進行自動裝配毫無意義),則嘗試找到對應的物件
  5. 找到這個屬性的寫方法
  6. 建立對應的 DependencyDescriptor 依賴注入描述物件,預設 required 為 false,表示找不到也沒關係
  7. 依賴注入,找到該屬性對應的物件,呼叫 resolveDependency(...) 方法
  8. 如果找到屬性物件,則將該其新增至 pvs
  9. 將注入的屬性物件和當前 Bean 之前的關係儲存起來

根據"物件"名稱通過 resolveDependency(...) 獲取到對應的物件,該方法就是依賴注入的底層實現,整個過程也非常複雜,所以將這部分內容放在下一篇《@Autowired 等註解的實現原理》文章中

屬性值的後置處理

呼叫 InstantiationAwareBeanPostProcessor#postProcessProperties 方法,對前面屬性值進行處理

在前面的AUTOWIRE_BY_NAMEAUTOWIRE_BY_TYPE兩種注入模式中,找到的都是普通物件的屬性值,例如 @Autowired、@Value 和 @Resource 註解並沒有被解析,且預設的注入模式還是AUTOWIRE_NO,那這些註解是如何被解析的呢?Spring 內部有下面兩個 InstantiationAwareBeanPostProcessor 處理器:

  • AutowiredAnnotationBeanPostProcessor,解析 @Autowired 和 @Value 註解標註的屬性,獲取對應屬性值
  • CommonAnnotationBeanPostProcessor,會解析 @Resource 註解標註的屬性,獲取對應的屬性值

這裡先提一下,具體的實現過程在下一篇《@Autowired 等註解的實現原理》文章中進行分析

屬性填充

applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) 方法,屬性填充

如果獲取到的 pvs 屬性值物件不為空,則將裡面的屬性值設定到當前 Bean 對應的屬性中(依賴注入),我們知道前面找到的屬性值並沒有設定到 Bean 中,且屬性值可能是一個表示式,型別也可能也不對,需要先進行處理和型別轉換,然後再設定到該例項物件中,方法如下:

// AbstractAutowireCapableBeanFactory.java
protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) {
    // <1> 沒有相關屬性值,則直接 `return` 返回
    if (pvs.isEmpty()) {
        return;
    }
    if (System.getSecurityManager() != null && bw instanceof BeanWrapperImpl) {
        ((BeanWrapperImpl) bw).setSecurityContext(getAccessControlContext());
    }

    // ------------------------開始屬性值的轉換與填充------------------------

    MutablePropertyValues mpvs = null;
    // 定義一個 `original` 集合,承載屬性值(未進行轉換)
    List<PropertyValue> original;

    // <2> 如果 `pvs` 是 MutablePropertyValues 型別,則可能已經處理過了
    if (pvs instanceof MutablePropertyValues) {
        mpvs = (MutablePropertyValues) pvs;
        if (mpvs.isConverted()) {
            // Shortcut: use the pre-converted values as-is.
            try {
                // <2.1> 屬性值已經轉換了,則將這些屬性值設定到當前 Bean 中(反射機制),依賴注入的最終實現!!!
                bw.setPropertyValues(mpvs);
                return;
            }
            catch (BeansException ex) {
                throw new BeanCreationException(
                        mbd.getResourceDescription(), beanName, "Error setting property values", ex);
            }
        }
        // <2.2> 沒有轉換過,則獲取所有的屬性值集合
        original = mpvs.getPropertyValueList();
    }
    else {
        // <2.2> 獲取所有的屬性值集合
        original = Arrays.asList(pvs.getPropertyValues());
    }

    // 獲取 TypeConverter 型別轉換器,用於取代預設的 PropertyEditor 型別轉換器
    // 例如 Spring 3.0 之後的 ConversionService
    TypeConverter converter = getCustomTypeConverter();
    if (converter == null) {
        converter = bw;
    }
    // 獲取對應的解析器
    BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this, beanName, mbd, converter);

    // Create a deep copy, resolving any references for values.
    // <3> 定義一個 `deepCopy` 集合,儲存轉換後的屬性值
    List<PropertyValue> deepCopy = new ArrayList<>(original.size());
    boolean resolveNecessary = false;
    // <4> 遍歷所有的屬性值,進行轉換(如果有必要)
    for (PropertyValue pv : original) {
        // <4.1> 已經轉換過,則直接新增到 `deepCopy` 中
        if (pv.isConverted()) {
            deepCopy.add(pv);
        }
        // <4.2> 否則,開始進行轉換
        else {
            String propertyName = pv.getName();
            // 轉換之前的屬性值
            Object originalValue = pv.getValue();
            // <4.2.1> 表示式的處理(如果有必要的話),例如你在 XML 配置的屬性值為 `${systenm.user}`,則會解析出對應的值
            Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
            // 轉換之後的屬性值
            Object convertedValue = resolvedValue;
            // 該屬性是否可以轉換
            boolean convertible = bw.isWritableProperty(propertyName) && // 屬性可寫
                    !PropertyAccessorUtils.isNestedOrIndexedProperty(propertyName); // 不包含 `.` 和 `[`
            if (convertible) {
                // <4.2.2> 使用型別轉換器轉換屬性值(如果有必要的話)
                convertedValue = convertForProperty(resolvedValue, propertyName, bw, converter);
            }
            // Possibly store converted value in merged bean definition,
            // in order to avoid re-conversion for every created bean instance.
            if (resolvedValue == originalValue) { // 屬性值沒有轉換過
                if (convertible) {
                    // <4.2.3> 設定轉換後的值,避免上面的各種判斷
                    pv.setConvertedValue(convertedValue);
                }
                // <4.2.4> 新增到 `deepCopy` 中
                deepCopy.add(pv);
            }
            // 屬否則屬性值進行了轉換
            else if (convertible // 可轉換的
                    && originalValue instanceof TypedStringValue // 屬性原始值是字串型別
                    && !((TypedStringValue) originalValue).isDynamic() // 屬性的原始型別值不是動態生成的字串
                    && !(convertedValue instanceof Collection || ObjectUtils.isArray(convertedValue))) // 屬性的原始值不是集合或者陣列型別
            {
                // <4.2.3> 設定轉換後的值,避免上面的各種判斷
                pv.setConvertedValue(convertedValue);
                // <4.2.4> 新增到 `deepCopy` 中
                deepCopy.add(pv);
            }
            // 否則
            else {
                // 這個屬性每次都要處理,不能快取
                resolveNecessary = true;
                // <4.2.4> 新增到 `deepCopy` 中
                deepCopy.add(new PropertyValue(pv, convertedValue));
            }
        }
    }
    // <5> 如果屬性值不為空,且不需要每次都處理,則設定為已轉換
    if (mpvs != null && !resolveNecessary) {
        mpvs.setConverted();
    }

    // Set our (possibly massaged) deep copy.
    try {
        // <6> 將屬性值設定到當前 Bean 中(反射機制),依賴注入的最終實現!!!
        bw.setPropertyValues(new MutablePropertyValues(deepCopy));
    }
    catch (BeansException ex) {
        throw new BeanCreationException(
                mbd.getResourceDescription(), beanName, "Error setting property values", ex);
    }
}

過程大致如下:

  1. 沒有相關屬性值,則直接 return 返回

開始屬性值的轉換與填充,先定義一個 original 集合,承載屬性值(未進行轉換)

  1. 如果 pvs 是 MutablePropertyValues 型別,則可能已經處理過了,否則,獲取所有的屬性值集合,放入 original 集合中

    1. 屬性值已經轉換了,則將這些屬性值設定到當前 Bean 中(反射機制),依賴注入的最終實現!!!

      呼叫 BeanWrapperImpl#setPropertyValues(PropertyValues) 方法

    2. 沒有轉換過,則獲取所有的屬性值集合,放入 original 集合中

  2. 定義一個 deepCopy 集合,儲存轉換後的屬性值

  3. 遍歷所有的屬性值,進行轉換(如果有必要)

    1. 已經轉換過,則直接新增到 deepCopy
    2. 否則,開始進行轉換
      1. 表示式的處理(如果有必要的話),例如你在 XML 配置的屬性值為 ${systenm.user},則會解析出對應的值
      2. 使用型別轉換器轉換屬性值(如果有必要的話)
      3. 設定轉換後的值,避免上面的各種判斷
      4. 新增到 deepCopy
  4. 如果屬性值不為空,且不需要每次都處理,則設定為已轉換

  5. 將屬性值設定到當前 Bean 中(反射機制),依賴注入的最終實現!!!

    呼叫 BeanWrapperImpl#setPropertyValues(PropertyValues) 方法


整個屬性注入過程非常複雜,上面僅列出了關鍵步驟,可以看到最終會呼叫 BeanWrapperImpl#setPropertyValues(PropertyValues) 方法將屬性值設定到 Bean 中

在 Bean 的例項化階段獲取到的就是一個 BeanWrapperImpl 物件,所以這裡呼叫的就是當前 Bean 的 setPropertyValues(PropertyValues) 方法,該方法的底層藉助於 Java Beans 的 java.beans.PropertyDescriptor 屬性描述器,獲取到對應的寫方法,然後通過反射機制設定當前 Bean 的屬性值

總結

當我們顯示或者隱式地呼叫AbstractBeanFactorygetBean(...) 方法時,會觸發 Bean 的載入,在《開啟 Bean 的載入》文章中分析了整個載入過程。

對於不同作用域的 Bean,底層都會呼叫 AbstractAutowireCapableBeanFactorycreateBean(...) 方法進行建立,在《Bean 的建立過程》文章中分析了整個建立過程。建立 Bean 的過程中,在獲取到的一個例項物件後,需要獲取相關屬性值,然後注入到 Bean 中,其中獲取屬性值有三種模式:

  • AUTOWIRE_NO,預設,不獲取相關屬性值

  • AUTOWIRE_BY_NAME,通過名稱獲取沒有定義屬性值的"物件"的屬性值,通過 getBean(String beanName) 查詢

  • AUTOWIRE_BY_TYPE,通過型別獲取沒有定義屬性值的"物件"的屬性值,依賴注入的方式

預設情況下,獲取到已定義的屬性值後不會通過上面的方式去找屬性值,在後續有一個屬性值的後置處理,會呼叫所有的 InstantiationAwareBeanPostProcessor 處理器的 postProcessProperties 方法進行處理,例如 Spring 內部有兩個 InstantiationAwareBeanPostProcessor 處理器:

  • AutowiredAnnotationBeanPostProcessor,解析 @Autowired 和 @Value 註解標註的屬性,獲取對應屬性值
  • CommonAnnotationBeanPostProcessor,會解析 @Resource 註解標註的屬性,獲取對應的屬性值

在獲取到所有的屬性值後然後通過反射機制設定到 Bean 中

關於 @Autowired、@Value 和 @Resource 註解的實現原理將在下一篇《@Autowired 等註解的實現原理》文章中進行分析

相關文章