Spring IOC源碼解析-填充屬性到bean原始對象

一.簡介

在這篇文章中,我們將會了解到Spring是如何將配置文件中的屬性值填充到bean對象中。對於填充屬性這個過程,簡單點來說,JavaBean 的每個屬性通常都有 getter/setter 方法,我們可以直接調用 setter 方法將屬性值設置進去。當然,這樣做還是太簡單了,填充屬性的過程中還有許多事情要做。比如在 Spring 配置中,所有屬性值都是以字符串的形式進行配置的,我們在將這些屬性值賦值給對象的成員變量時,要根據變量類型進行相應的類型轉換。對於一些集合類的配置,比如、和,還要將這些配置轉換成相應的集合對象才能進行後續的操作。除此之外,如果用戶配置了自動注入(autowire = byName/byType),Spring 還要去爲自動注入的屬性尋找合適的注入項。由此可以見,屬性填充的整個過程還是很複雜的,並非是簡單調用 setter 方法設置屬性值即可。

接下來,我們深入到源碼中,從源碼中瞭解屬性填充的整個過程。

二.源碼分析

2.1 populateBean源碼

protected void populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper bw) {
    // 獲取屬性列表
    PropertyValues pvs = mbd.getPropertyValues();

    if (bw == null) {
        if (!pvs.isEmpty()) {
            throw new BeanCreationException(
                    mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance");
        }
        else {
            return;
        }
    }

    boolean continueWithPropertyPopulation = true;
    /*
     * 在屬性被填充前,給 InstantiationAwareBeanPostProcessor 類型的後置處理器一個修改 
     * bean 狀態的機會。關於這段後置引用,官方的解釋是:讓用戶可以自定義屬性注入。比如用戶實現一
     * 個 InstantiationAwareBeanPostProcessor 類型的後置處理器,並通過 
     * postProcessAfterInstantiation 方法向 bean 的成員變量注入自定義的信息。當然,如果無
     * 特殊需求,直接使用配置中的信息注入即可。另外,Spring 並不建議大家直接實現 
     * InstantiationAwareBeanPostProcessor 接口,如果想實現這種類型的後置處理器,更建議
     * 通過繼承 InstantiationAwareBeanPostProcessorAdapter 抽象類實現自定義後置處理器。
     */
    if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
        for (BeanPostProcessor bp : getBeanPostProcessors()) {
            if (bp instanceof InstantiationAwareBeanPostProcessor) {
                InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
                if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
                    continueWithPropertyPopulation = false;
                    break;
                }
            }
        }
    }

    /* 
     * 如果上面設置 continueWithPropertyPopulation = false,表明用戶可能已經自己填充了
     * bean 的屬性,不需要 Spring 幫忙填充了。此時直接返回即可
     */
    if (!continueWithPropertyPopulation) {
        return;
    }

    // 根據名稱或類型注入依賴
    if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME ||
            mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
        MutablePropertyValues newPvs = new MutablePropertyValues(pvs);

        // 通過屬性名稱注入依賴
        if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME) {
            autowireByName(beanName, mbd, bw, newPvs);
        }

        // 通過屬性類型注入依賴
        if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
            autowireByType(beanName, mbd, bw, newPvs);
        }

        pvs = newPvs;
    }

    boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
    boolean needsDepCheck = (mbd.getDependencyCheck() != RootBeanDefinition.DEPENDENCY_CHECK_NONE);

    /*
     * 這裏又是一種後置處理,用於在 Spring 填充屬性到 bean 對象前,對屬性的值進行相應的處理,
     * 比如可以修改某些屬性的值。這時注入到 bean 中的值就不是配置文件中的內容了,
     * 而是經過後置處理器修改後的內容
     */ 
    if (hasInstAwareBpps || needsDepCheck) {
        PropertyDescriptor[] filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
        if (hasInstAwareBpps) {
            for (BeanPostProcessor bp : getBeanPostProcessors()) {
                if (bp instanceof InstantiationAwareBeanPostProcessor) {
                    InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
                    // 對屬性進行後置處理
                    pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
                    if (pvs == null) {
                        return;
                    }
                }
            }
        }
        if (needsDepCheck) {
            checkDependencies(beanName, mbd, filteredPds, pvs);
        }
    }

    // 應用屬性值到 bean 對象中
    applyPropertyValues(beanName, mbd, bw, pvs);
}

在populateBean函數中提供了這樣的處理流程:

  1. 獲取屬性列表 pvs
  2. 在屬性被填充到 bean 前,應用後置處理自定義屬性填充。InstantiationAwareBeanPostProcessor處理器的postProcessAfterInstantiation函數的應用,此函數可以控制程序是否繼續進行屬性填充。
  3. 根據注入類型(byName/byType),提取依賴的bean,並統一存入PropertyValues中。
  4. 應用InstantiationAwareBeanPostProcessor處理器的postProcessPropertyValues方法,對屬性獲取完畢填充前對屬性的再次處理。
  5. 將所有的PropertyValues中的屬性填充至BeanWrapper中。

注意第3步,也就是根據名稱或類型解析相關依賴(autowire)。該邏輯只會解析依賴,並不會將解析出的依賴立即注入到 bean 對象中。所有的屬性值是在 applyPropertyValues 方法中統一被注入到 bean 對象中的。

下來對依賴注入(autowireByName/autowireByType)以及屬性填充進一步分析實現細節。

2.2 autowireByName 方法分析

protected void autowireByName(
        String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {

    /*
     * 獲取非簡單類型屬性的名稱,且該屬性未被配置在配置文件中。這裏從反面解釋一下什麼是"非簡單類型"
     * 屬性,我們先來看看 Spring 認爲的"簡單類型"屬性有哪些,如下:
     *   1. CharSequence 接口的實現類,比如 String
     *   2. Enum
     *   3. Date
     *   4. URI/URL
     *   5. Number 的繼承類,比如 Integer/Long
     *   6. byte/short/int... 等基本類型
     *   7. Locale
     *   8. 以上所有類型的數組形式,比如 String[]、Date[]、int[] 等等
     * 
     * 除了要求非簡單類型的屬性外,還要求屬性未在配置文件中配置過,也就是 pvs.contains(pd.getName()) = false。
     */
    String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
    for (String propertyName : propertyNames) {
        // 檢測是否存在與 propertyName 相關的 bean 或 BeanDefinition。若存在,則調用 BeanFactory.getBean 方法獲取 bean 實例
        if (containsBean(propertyName)) {
            // 從容器中獲取相應的 bean 實例
            Object bean = getBean(propertyName);
            // 將解析出的 bean 存入到屬性值列表(pvs)中
            pvs.add(propertyName, bean);
            registerDependentBean(propertyName, beanName);
            if (logger.isDebugEnabled()) {
                logger.debug("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");
            }
        }
    }
}

這個方法的邏輯比較簡單,該方法首先獲取非簡單類型屬性的名稱,然後再根據名稱到容器中獲得對應的bean實例,最後再將獲取的bean添加到屬性列表中。

2.3 autowireByType 方法分析

相比較autowireByName,autowireByType 則要複雜一些,複雜之處在於解析依賴的過程。我們複雜的地方當做一個黑盒,只需關注這個黑盒實現的功能即可。

protected void autowireByType(
        String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {

    TypeConverter converter = getCustomTypeConverter();
    if (converter == null) {
        converter = bw;
    }

    Set<String> autowiredBeanNames = new LinkedHashSet<String>(4);
    // 獲取非簡單類型的屬性
    String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
    for (String propertyName : propertyNames) {
        try {
            PropertyDescriptor pd = bw.getPropertyDescriptor(propertyName);
            // 如果屬性類型爲 Object,則忽略,不做解析
            if (Object.class != pd.getPropertyType()) {
                /*
                 * 獲取 setter 方法(write method)的參數信息,比如參數在參數列表中的
                 * 位置,參數類型,以及該參數所歸屬的方法等信息
                 */
                MethodParameter methodParam = BeanUtils.getWriteMethodParameter(pd);

                // Do not allow eager init for type matching in case of a prioritized post-processor.
                boolean eager = !PriorityOrdered.class.isAssignableFrom(bw.getWrappedClass());
                // 創建依賴描述對象
                DependencyDescriptor desc = new AutowireByTypeDependencyDescriptor(methodParam, eager);
                /*
                 * 下面的方法用於解析依賴。過程比較複雜,先把這裏看成一個黑盒,我們只要知道這
                 * 個方法可以幫我們解析出合適的依賴即可。
                 */
                Object autowiredArgument = resolveDependency(desc, beanName, autowiredBeanNames, converter);
                if (autowiredArgument != null) {
                    // 將解析出的 bean 存入到屬性值列表(pvs)中
                    pvs.add(propertyName, autowiredArgument);
                }
                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);
        }
    }
}

autowireByType首先也是獲得非簡單類型屬性的名稱,然後再根據屬性名稱獲得屬性描述符,並由屬性描述符獲取方法參數對象MethodParameter,隨後在根據MethodParameter對象獲取依賴描述符對象,整個過程爲beanName->PropertyDescriptor->MethodParameter->DependencyDescriptor。在獲取到依賴描述符對象後,再根據依賴描述符對象解析出合適的依賴,最後將解析出的結果存入屬性列表pvs中即可。

2.4 解析依賴

2.4.1 resolveDependency

resolveDependency 依賴查找解決了以下場景:

  1. Optional:JDK8 提供了 API。主要是將依賴設置非強制依賴,即 descriptor.required=false。
  2. 延遲依賴注入支持:ObjectFactory、ObjectProvider、javax.inject.Provider 沒有本質的區別。
  3. 另一種延遲注入的支持 - @Lazy 屬性。
  4. 根據類型查找依賴 - doResolveDependency。
public Object resolveDependency(DependencyDescriptor descriptor, String requestingBeanName,
        Set<String> autowiredBeanNames, TypeConverter typeConverter) throws BeansException {

    descriptor.initParameterNameDiscovery(getParameterNameDiscoverer());
    if (javaUtilOptionalClass == descriptor.getDependencyType()) {
        return new OptionalDependencyFactory().createOptionalDependency(descriptor, requestingBeanName);
    }
    else if (ObjectFactory.class == descriptor.getDependencyType() ||
            ObjectProvider.class == descriptor.getDependencyType()) {
        return new DependencyObjectProvider(descriptor, requestingBeanName);
    }
    else if (javaxInjectProviderClass == descriptor.getDependencyType()) {
        return new Jsr330ProviderFactory().createDependencyProvider(descriptor, requestingBeanName);
    }
    else {
        Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(
                descriptor, requestingBeanName);
        if (result == null) {
            // 解析依賴
            result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
        }
        return result;
    }
}

2.4.2 doResolveDependency

doResolveDependency 封裝了依賴查找的各種情況:

  1. 快速查找: @Autowired 註解處理場景。AutowiredAnnotationBeanPostProcessor 處理 @Autowired 註解時,如果注入的對象只有一個,會將該 bean 對應的名稱緩存起來,下次直接通過名稱查找會快很多。
  2. 注入指定值:@Value 註解處理場景。QualifierAnnotationAutowireCandidateResolver 處理 @Value 註解時,會讀取 @Value 對應的值進行注入。如果是 String 要經過三個過程:①佔位符處理 -> ②EL 表達式解析 -> ③類型轉換,這也是一般的處理過程,BeanDefinitionValueResolver 處理 String 對象也是這個過程。
  3. 集合依賴查詢:直接全部委託給 resolveMultipleBeans 方法。
  4. 單個依賴查詢:先調用 findAutowireCandidates 查找所有可用的依賴,如果有多個依賴,則根據規則匹配: @Primary -> @Priority -> ③方法名稱或字段名稱。
public Object doResolveDependency(DependencyDescriptor descriptor, String beanName,
        Set<String> autowiredBeanNames, TypeConverter typeConverter) throws BeansException {

    InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor);
    try {
        // 該方法最終調用了 beanFactory.getBean(String, Class),從容器中獲取依賴
        Object shortcut = descriptor.resolveShortcut(this);
        // 如果容器中存在所需依賴,這裏進行斷路操作,提前結束依賴解析邏輯
        if (shortcut != null) {
            return shortcut;
        }

        Class<?> type = descriptor.getDependencyType();
        // 處理 @value 註解
        Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);
        if (value != null) {
            if (value instanceof String) {
                String strVal = resolveEmbeddedValue((String) value);
                BeanDefinition bd = (beanName != null && containsBean(beanName) ? getMergedBeanDefinition(beanName) : null);
                value = evaluateBeanDefinitionString(strVal, bd);
            }
            TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
            return (descriptor.getField() != null ?
                    converter.convertIfNecessary(value, type, descriptor.getField()) :
                    converter.convertIfNecessary(value, type, descriptor.getMethodParameter()));
        }

        // 解析數組、list、map 等類型的依賴
        Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);
        if (multipleBeans != null) {
            return multipleBeans;
        }

        /*
         * 按類型查找候選列表,如果某個類型已經被實例化,則返回相應的實例。
         * 比如下面的配置:
         *
         *   <bean name="mongoDao" class="xyz.coolblog.autowire.MongoDao" primary="true"/>
         *   <bean name="service" class="xyz.coolblog.autowire.Service" autowire="byType"/>
         *   <bean name="mysqlDao" class="xyz.coolblog.autowire.MySqlDao"/>
         *
         * MongoDao 和 MySqlDao 均實現自 Dao 接口,Service 對象(不是接口)中有一個 Dao 
         * 類型的屬性。現在根據類型自動注入 Dao 的實現類。這裏有兩個候選 bean,一個是 
         * mongoDao,另一個是 mysqlDao,其中 mongoDao 在 service 之前實例化,
         * mysqlDao 在 service 之後實例化。此時 findAutowireCandidates 方法會返回如下的結果:
         *
         *   matchingBeans = [ <mongoDao, Object@MongoDao>, <mysqlDao, Class@MySqlDao> ]
         *
         * 注意 mysqlDao 還未實例化,所以返回的是 MySqlDao.class。
         * 
         * findAutowireCandidates 這個方法邏輯比較複雜,我簡單說一下它的工作流程吧,如下:
         *   1. 從 BeanFactory 中獲取某種類型 bean 的名稱,比如上面的配置中 
         *      mongoDao 和 mysqlDao 均實現了 Dao 接口,所以他們是同一種類型的 bean。
         *   2. 遍歷上一步得到的名稱列表,並判斷 bean 名稱對應的 bean 是否是合適的候選項,
         *      若合適則添加到候選列表中,並在最後返回候選列表
         *      
         */
        Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
        if (matchingBeans.isEmpty()) {
            if (isRequired(descriptor)) {
                // 拋出 NoSuchBeanDefinitionException 異常
                raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
            }
            return null;
        }

        String autowiredBeanName;
        Object instanceCandidate;

        if (matchingBeans.size() > 1) {
            /*
             * matchingBeans.size() > 1,則表明存在多個可注入的候選項,這裏判斷使用哪一個
             * 候選項。比如下面的配置:
             *
             *   <bean name="mongoDao" class="xyz.coolblog.autowire.MongoDao" primary="true"/>
             *   <bean name="mysqlDao" class="xyz.coolblog.autowire.MySqlDao"/>
             *
             * mongoDao 的配置中存在 primary 屬性,所以 mongoDao 會被選爲最終的候選項。如
             * 果兩個 bean 配置都沒有 primary 屬性,則需要根據優先級選擇候選項。優先級這一塊
             * 的邏輯沒細看,不多說了。
             */
            autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
            if (autowiredBeanName == null) {
                if (isRequired(descriptor) || !indicatesMultipleBeans(type)) {
                    // 拋出 NoUniqueBeanDefinitionException 異常
                    return descriptor.resolveNotUnique(type, matchingBeans);
                }
                else {
                    return null;
                }
            }
            // 根據解析出的 autowiredBeanName,獲取相應的候選項
            instanceCandidate = matchingBeans.get(autowiredBeanName);
        }
        else { // 只有一個候選項,直接取出來即可
            Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next();
            autowiredBeanName = entry.getKey();
            instanceCandidate = entry.getValue();
        }

        if (autowiredBeanNames != null) {
            autowiredBeanNames.add(autowiredBeanName);
        }

        // 返回候選項實例,如果實例是 Class 類型,則調用 beanFactory.getBean(String, Class) 獲取相應的 bean。否則直接返回即可
        return (instanceCandidate instanceof Class ?
                descriptor.resolveCandidate(autowiredBeanName, type, this) : instanceCandidate);
    }
    finally {
        ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint);
    }
}

總結一下 doResolveDependency 的執行流程吧,如下:

  1. 首先將 beanName 和 requiredType 作爲參數,並嘗試從 BeanFactory 中獲取與此對於的 bean。若獲取成功,就可以提前結束 doResolveDependency 的邏輯。
  2. 處理 @value 註解
  3. 解析數組、List、Map 等類型的依賴,如果解析結果不爲空,則返回結果
  4. 根據類型查找合適的候選項
  5. 如果候選項的數量爲0,則拋出異常。爲1,直接從候選列表中取出即可。若候選項數量 > 1,則在多個候選項中確定最優候選項,若無法確定則拋出異常
  6. 若候選項是 Class 類型,表明候選項還沒實例化,此時通過 BeanFactory.getBean 方法對其進行實例化。若候選項是非 Class 類型,則表明已經完成了實例化,此時直接返回即可。

 doResolveDependency 方法的四個功能,快速查找和集合處理都委託給了其它方法,注入指定值雖然看起來複雜,但佔位符處理、EL 表達式解析、類型轉換這三個功能點都有具體的類處理,也不是本文的重點。

重點看一下單個依賴的查詢,弄明白了單個依賴的查詢,其它集合依賴也差不多。

  1. 查找容器中所有可用依賴:findAutowireCandidates 方法根據類型查找依賴。
  2. 如何有多個依賴怎麼處理?其實 Spring 有一套通用的流程,先按 @Primary 查找,再按 @Priority,最後按方法名稱或字段名稱查找,直到只有一個 bean 爲止。相關的匹配規則見 determineAutowireCandidate 方法。
  3. 此時只有一個依賴,從容器獲取真實的 bean。descriptor.resolveCandidate 方法根據名稱 autowiredBeanName 實例化對象。

思考:findAutowireCandidates 返回的爲什麼是對象類型,而不是實例對象?

Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);

matchingBeans 中的 Object 對象可能是對象類型,而不全部是實例對象。因爲 findAutowireCandidates 方法是根據類型 type 查找名稱 beanNames,如果容器中該 beanName 還沒有實例化,findAutowireCandidates 不會畫蛇添足直接實例化該 bean,當然如果已經實例化了會直接返回這個 bean。

2.4.3 findAutowireCandidates

根據上面的分析,resolveDependency 方法對 Optional、延遲注入、懶加載注入等分別進行了處理。之後 doResolveDependency 在正式查找之前看能不能快速查找,如緩存 beanName、@Value 等快速指定需要注入的值,避免通過類型查找,最後纔對集合依賴和單一依賴分別進行了處理。實際上,無論是集合依賴還是單一依賴查找,本質上都是調用 findAutowireCandidates 進行類型依賴查找。

protected Map<String, Object> findAutowireCandidates(
    @Nullable String beanName, Class<?> requiredType, DependencyDescriptor descriptor) {
    
    Map<String, Object> result = new LinkedHashMap<>(candidateNames.length);
    // 1. Spring IoC 內部依賴 resolvableDependencies
    for (Map.Entry<Class<?>, Object> classObjectEntry : this.resolvableDependencies.entrySet()) {
        Class<?> autowiringType = classObjectEntry.getKey();
        if (autowiringType.isAssignableFrom(requiredType)) {
            Object autowiringValue = classObjectEntry.getValue();
            autowiringValue = AutowireUtils.resolveAutowiringValue(autowiringValue, requiredType);
            if (requiredType.isInstance(autowiringValue)) {
                result.put(ObjectUtils.identityToString(autowiringValue), autowiringValue);
                break;
            }
        }
    }
    
    // 2. 類型查找:本質上遞歸調用beanFactory#beanNamesForType。先匹配實例類型,再匹配bd。
    String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
        this, requiredType, true, descriptor.isEager());
    for (String candidate : candidateNames) {
        // 2.1 isSelfReference說明beanName和candidate本質是同一個對象
        //     isAutowireCandidate進一步匹配bd.autowireCandidate、泛型、@@Qualifier等進行過濾
        if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, descriptor)) {
            // 2.2 添加到候選對象中
            addCandidateEntry(result, candidate, descriptor, requiredType);
        }
    }
    
    // 3. 補償機制:如果依賴查找無法匹配,怎麼辦?包含泛型補償和自身引用補償兩種。
    if (result.isEmpty()) {
        boolean multiple = indicatesMultipleBeans(requiredType);
        // 3.1 fallbackDescriptor: 泛型補償,實際上是允許注入對象類型的泛型存在無法解析的情況
        DependencyDescriptor fallbackDescriptor = descriptor.forFallbackMatch();
        // 3.2 補償1:不允許自稱依賴,但如果是集合依賴,需要過濾非@Qualifier對象。什麼場景?
        for (String candidate : candidateNames) {
            if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, fallbackDescriptor) &&
                (!multiple || getAutowireCandidateResolver().hasQualifier(descriptor))) {
                addCandidateEntry(result, candidate, descriptor, requiredType);
            }
        }
        // 3.3 補償2:允許自稱依賴,但如果是集合依賴,注入的集合依賴中需要過濾自己
        if (result.isEmpty() && !multiple) {
            for (String candidate : candidateNames) {
                if (isSelfReference(beanName, candidate) &&
                    (!(descriptor instanceof MultiElementDescriptor) || !beanName.equals(candidate)) &&
                    isAutowireCandidate(candidate, fallbackDescriptor)) {
                    addCandidateEntry(result, candidate, descriptor, requiredType);
                }
            }
        }
    }
    return result;
}

 findAutowireCandidates 大致可以分爲三步:先查找內部依賴,再根據類型查找,最後沒有可注入的依賴則進行補償。

  1. 查找內部依賴:Spring IoC 容器本身相關依賴,這部分內容是用戶而言是透明的,也不用感知。resolvableDependencies 集合中註冊如 BeanFactory、ApplicationContext 、ResourceLoader、ApplicationEventPublisher 等。
  2. 根據類型查找:包括 ①外部託管 Bean ②註冊 BeanDefinition。類型查找調用 beanFactory#beanNamesForType 方法。
    • 自身引用:isSelfReference 方法判斷 beanName 和 candidate 是否是同一個對象,包括兩種情況:一是名稱完全相同,二是 candidate 對應的工廠對象創建了 beanName。
    • 是否可以注入:底層實際調用 resolver.isAutowireCandidate 方法進行過濾,包含三重規則:①bd.autowireCandidate=true -> ②泛型匹配 -> ③@Qualifier。
  3. 補償機制:如果依賴查找無法匹配,怎麼辦?Spring 提供了兩種補償機制:一是泛型補償,允許注入對象對象的泛型無法解析,二是自身引用補償,對這兩種機制使用如下:
    • 先使用泛型補償,不允許自身引用:即 fallbackDescriptor。此時如果是集合依賴,對象必須是 @Qualifier 類型。
    • 允許泛型補償和自身引用補償:但如果是集合依賴,必須過濾自己本身,即 beanName.equals(candidate) 必須剔除。

這個方法中調用的方法沒有詳細展開,想要了解的可以自己看看。

2.5 applyPropertyValues 方法分析

程序運行到這裏,已經完成了對所有注入屬性的獲取,但是獲取的屬性是以PropertyValues形式存在的,還並沒有應用到已經實例化的bean中,這一個工作是在applyPropertyValues方法中。

protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) {
    if (pvs == null || pvs.isEmpty()) {
        return;
    }

    if (System.getSecurityManager() != null && bw instanceof BeanWrapperImpl) {
        ((BeanWrapperImpl) bw).setSecurityContext(getAccessControlContext());
    }

    MutablePropertyValues mpvs = null;
    List<PropertyValue> original;

    //檢測屬性列表是否已經轉換過了,若轉換了,直接填充屬性
    if (pvs instanceof MutablePropertyValues) {
        mpvs = (MutablePropertyValues) pvs;
        // 如果屬性列表 pvs 被轉換過,則直接返回即可
        if (mpvs.isConverted()) {
            try {
                bw.setPropertyValues(mpvs);
                return;
            }
            catch (BeansException ex) {
                throw new BeanCreationException(
                        mbd.getResourceDescription(), beanName, "Error setting property values", ex);
            }
        }
        original = mpvs.getPropertyValueList();
    }
    else {
        original = Arrays.asList(pvs.getPropertyValues());
    }

    TypeConverter converter = getCustomTypeConverter();
    if (converter == null) {
        converter = bw;
    }
      //獲取BeanDefinitionValueResolver,該Bean用於將bean定義對象中包含的值解析爲應用於目標bean實例的實際值。
    BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this, beanName, mbd, converter);
     // 用於存放實際解析後的屬性集合
    List<PropertyValue> deepCopy = new ArrayList<PropertyValue>(original.size());
    boolean resolveNecessary = false;
    // 遍歷屬性列表,解析原始值originalValue得到resolvedValue
    for (PropertyValue pv : original) {
        // 如果屬性值被轉換過,則就不需要再次轉換
        if (pv.isConverted()) {
            deepCopy.add(pv);
        }
        else {
                // 若該屬性沒有被解析過
            String propertyName = pv.getName(); // 屬性名稱
            Object originalValue = pv.getValue(); // 屬性未經類型轉換的值
            /*
             * 解析屬性值。舉例說明,先看下面的配置:
             * 
             *   <bean id="macbook" class="MacBookPro">
             *       <property name="manufacturer" value="Apple"/>
             *       <property name="width" value="280"/>
             *       <property name="cpu" ref="cpu"/>
             *       <property name="interface">
             *           <list>
             *               <value>USB</value>
             *               <value>HDMI</value>
             *               <value>Thunderbolt</value>
             *           </list>
             *       </property>
             *   </bean>
             *
             * 上面是一款電腦的配置信息,每個 property 配置經過下面的方法解析後,返回如下結果:
             *   propertyName = "manufacturer", resolvedValue = "Apple"
             *   propertyName = "width", resolvedValue = "280"
             *   propertyName = "cpu", resolvedValue = "CPU@1234"  注:resolvedValue 是一個對象
             *   propertyName = "interface", resolvedValue = ["USB", "HDMI", "Thunderbolt"]
             *
             * 如上所示,resolveValueIfNecessary 會將 ref 解析爲具體的對象,將 <list> 
             * 標籤轉換爲 List 對象等。對於 int 類型的配置,這裏並未做轉換,所以 
             * width = "280",還是字符串。除了解析上面幾種類型,該方法還會解析 <set/>、
             * <map/>、<array/> 等集合配置
             */
            Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
            Object convertedValue = resolvedValue;

            /*
             * convertible 表示屬性值是否可轉換,由兩個條件合成而來。第一個條件不難理解,解釋
             * 一下第二個條件。第二個條件用於檢測 propertyName 是否是 nested 或者 indexed,
             * 直接舉例說明吧:
             * 
             *   public class Room {
             *       private Door door = new Door();
             *   }
             *
             * room 對象裏面包含了 door 對象,如果我們想向 door 對象中注入屬性值,則可以這樣配置:
             *
             *   <bean id="room" class="xyz.coolblog.Room">
             *      <property name="door.width" value="123"/>
             *   </bean>
             * 
             * isNestedOrIndexedProperty 會根據 propertyName 中是否包含 . 或 [  返回 
             * true 和 false。包含則返回 true,否則返回 false。
             * 關於 nested 類型的屬性,大家還可以參考 Spring 的官方文檔:
             *     https://docs.spring.io/spring/docs/4.3.17.RELEASE/spring-framework-reference/htmlsingle/#beans-beans-conventions
             */
            boolean convertible = bw.isWritableProperty(propertyName) &&
                    !PropertyAccessorUtils.isNestedOrIndexedProperty(propertyName);
            // 對於一般的屬性,convertible 通常爲 true
            if (convertible) {
                // 對屬性值的類型進行轉換,比如將 String 類型的屬性值 "123" 轉爲 Integer 類型的 123
                convertedValue = convertForProperty(resolvedValue, propertyName, bw, converter);
            }

            /*
             * 如果 originalValue 是通過 autowireByType 或 autowireByName 解析而來,
             * 那麼此處條件成立,即 (resolvedValue == originalValue) = true
             */
            if (resolvedValue == originalValue) {
                if (convertible) {
                    // 可能將轉換後的值存儲在merged bean definition中,避免對每個創建的bean實例進行重新轉換。
                    pv.setConvertedValue(convertedValue);
                }
                deepCopy.add(pv);
            }
            /*
             * 如果原始值 originalValue 是 TypedStringValue,且轉換後的值 
             * convertedValue 不是 Collection 或數組類型,則將轉換後的值存入到 pv 中。
             */
            else if (convertible && originalValue instanceof TypedStringValue &&
                    !((TypedStringValue) originalValue).isDynamic() &&
                    !(convertedValue instanceof Collection || ObjectUtils.isArray(convertedValue))) {
                pv.setConvertedValue(convertedValue);
                deepCopy.add(pv);
            }
            else {
                resolveNecessary = true;
                deepCopy.add(new PropertyValue(pv, convertedValue));
            }
        }
    }
    if (mpvs != null && !resolveNecessary) {
        mpvs.setConverted();
    }

    try {
        // 將所有的屬性值設置到 bean 實例中
        bw.setPropertyValues(new MutablePropertyValues(deepCopy));
    }
    catch (BeansException ex) {
        throw new BeanCreationException(
                mbd.getResourceDescription(), beanName, "Error setting property values", ex);
    }
}

這個方法的執行流程如下:

  1. 檢測屬性值列表是否已轉換過的,若轉換過,則直接填充屬性,無需再次轉換
  2. 遍歷屬性值列表 pvs,解析原始值 originalValue,得到解析值 resolvedValue
  3. 對解析後的屬性值 resolvedValue 進行類型轉換
  4. 將類型轉換後的屬性值設置到 PropertyValue 對象中,並將 PropertyValue 對象存入 deepCopy 集合中
  5. 將 deepCopy 中的屬性信息注入到 bean 對象中

參考:填充屬性到 bean 原始對象

Spring IoC 依賴注入(三)resolveDependency

《Spring源碼深度解析》

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