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

1. 簡介

本篇文章,咱們來一塊兒瞭解一下 Spring 是如何將配置文件中的屬性值填充到 bean 對象中的。我在前面幾篇文章中介紹過 Spring 建立 bean 的流程,即 Spring 先經過反射建立一個原始的 bean 對象,而後再向這個原始的 bean 對象中填充屬性。對於填充屬性這個過程,簡單點來講,JavaBean 的每一個屬性一般都有 getter/setter 方法,咱們能夠直接調用 setter 方法將屬性值設置進去。固然,這樣作仍是太簡單了,填充屬性的過程當中還有許多事情要作。好比在 Spring 配置中,全部屬性值都是以字符串的形式進行配置的,咱們在將這些屬性值賦值給對象的成員變量時,要根據變量類型進行相應的類型轉換。對於一些集合類的配置,好比 ,還要將這些配置轉換成相應的集合對象才能進行後續的操做。除此以外,若是用戶配置了自動注入(autowire = byName/byType),Spring 還要去爲自動注入的屬性尋找合適的注入項。由此能夠見,屬性填充的整個過程仍是很複雜的,並不是是簡單調用 setter 方法設置屬性值便可。html

關於屬性填充的一些知識,本章先介紹這裏。接下來,咱們深刻到源碼中,從源碼中瞭解屬性填充的整個過程。java

2. 源碼分析

2.1 populateBean 源碼一覽

本節,咱們先來看一下填充屬性的方法,即 populateBean。該方法並不複雜,但它所調用的一些方法比較複雜。不過好在咱們這裏只須要知道這些方法都有什麼用就好了,暫時不用糾結細節。好了,下面看源碼吧。mysql

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);
}

上面的源碼註釋的比較詳細了,下面咱們來總結一下這個方法的執行流程。以下:spring

  1. 獲取屬性列表 pvs
  2. 在屬性被填充到 bean 前,應用後置處理自定義屬性填充
  3. 根據名稱或類型解析相關依賴
  4. 再次應用後置處理,用於動態修改屬性列表 pvs 的內容
  5. 將屬性應用到 bean 對象中

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

在下面的章節中,我將會對 populateBean 方法中比較重要的幾個方法調用進行分析,也就是第3步和第5步中的三個方法。好了,本節先到這裏。數組

2.2 autowireByName 方法分析

本節來分析一下 autowireByName 方法的代碼,其實這個方法根據方法名,你們應該知道它有什麼用了。因此我也就不囉嗦了,我們直奔主題,直接分析源碼:mvc

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");
            }
        }
    }
}

autowireByName 方法的邏輯比較簡單,該方法首先獲取非簡單類型屬性的名稱,而後再根據名稱到容器中獲取相應的 bean 實例,最後再將獲取到的 bean 添加到屬性列表中便可。既然這個方法比較簡單,那我也就很少說了,繼續下面的分析。app

2.3 autowireByType 方法分析

本節咱們來分析一下 autowireByName 的孿生兄弟 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 的代碼自己並不複雜。和 autowireByName 同樣,autowireByType 首先也是獲取非簡單類型屬性的名稱。而後再根據屬性名獲取屬性描述符,並由屬性描述符獲取方法參數對象 MethodParameter,隨後再根據 MethodParameter 對象獲取依賴描述符對象,整個過程爲 beanName → PropertyDescriptor → MethodParameter → DependencyDescriptor。在獲取到依賴描述符對象後,再根據依賴描述符解析出合適的依賴。最後將解析出的結果存入屬性列表 pvs 中便可。ide

關於 autowireByType 方法中出現的幾種描述符對象,你們本身去看一下他們的實現吧,我就不分析了。接下來,咱們來分析一下解析依賴的方法 resolveDependency。以下:

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;
    }
}

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 是不是合適的候選項,
         *      若合適則添加到候選列表中,並在最後返回候選列表
         *      
         * findAutowireCandidates 比較複雜,我並未徹底搞懂,就不深刻分析了。見諒
         */
        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 這個方法仍是挺複雜的。這裏我就不繼續分析 doResolveDependency 所調用的方法了,對於這些方法,我也是似懂非懂。好了,本節的最後咱們來總結一下 doResolveDependency 的執行流程吧,以下:

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

好了,本節的內容先到這裏。若是有分析錯的地方,歡迎你們指出來。

2.4 applyPropertyValues 方法分析

通過了上面的流程,如今終於能夠將屬性值注入到 bean 對象中了。固然,這裏還不能當即將屬性值注入到對象中,由於在 Spring 配置文件中屬性值都是以 String 類型進行配置的,因此 Spring 框架須要對 String 類型進行轉換。除此以外,對於 ref 屬性,這裏還須要根據 ref 屬性值解析依賴。還有一些其餘操做,這裏就很少說了,更多的信息咱們一塊兒在源碼探尋。

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 valueResolver = new BeanDefinitionValueResolver(this, beanName, mbd, converter);

    List<PropertyValue> deepCopy = new ArrayList<PropertyValue>(original.size());
    boolean resolveNecessary = false;
    // 遍歷屬性列表
    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 類型的屬性,我
             * 沒在實踐中用過,因此不知道上面舉的例子是否是合理。若不合理,歡迎指正,也請多多指教。
             * 關於 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) {
                    // 將 convertedValue 設置到 pv 中,後續再次建立同一個 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);
    }
}

以上就是 applyPropertyValues 方法的源碼,配合着我寫的註釋,應該能夠理解這個方法的流程。這個方法也調用了不少其餘的方法,若是你們跟下去的話,會發現這些方法的調用棧也是很深的,比較複雜。這裏說一下 bw.setPropertyValues 這個方法,若是你們跟到這個方法的調用棧的最底部,會發現這個方法是經過調用對象的 setter 方法進行屬性設置的。這裏貼一下簡化後的代碼:

public class BeanWrapperImpl extends AbstractNestablePropertyAccessor implements BeanWrapper {

    // 省略部分代碼

    private class BeanPropertyHandler extends PropertyHandler {
        @Override
        public void setValue(final Object object, Object valueToApply) throws Exception {
            // 獲取 writeMethod,也就是 setter 方法
            final Method writeMethod = this.pd.getWriteMethod();
            if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers()) && !writeMethod.isAccessible()) {
                writeMethod.setAccessible(true);
            }
            final Object value = valueToApply;
            // 調用 setter 方法,getWrappedInstance() 返回的是 bean 對象
            writeMethod.invoke(getWrappedInstance(), value);
        }
    }
}

好了,本節的最後來總結一下 applyPropertyValues 方法的執行流程吧,以下:

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

3. 總結

本文對 populateBean 方法及其所調用的 autowireByName、autowireByType 和 applyPropertyValues 作了較爲詳細的分析,不知道你們看完後感受如何。我說一下個人感覺吧,從我看 Spring IOC 部分的源碼到如今寫了5篇關於 IOC 部分的源碼分析文章,整體感受 Spring 的源碼仍是很複雜的,調用層次很深。若是想對源碼有一個比較好的理解,須要很多的時間去分析,調試源碼。總的來講,不容易。固然,個人水平有限。若是你們本身去閱讀源碼,可能會以爲也沒這麼難啊。

好了,其餘的就很少說了。若是本文中有分析錯的地方,歡迎你們指正。最後感謝你們的閱讀。

附錄:Spring 源碼分析文章列表

Ⅰ. IOC

更新時間 標題
2018-05-30 Spring IOC 容器源碼分析系列文章導讀
2018-06-01 Spring IOC 容器源碼分析 - 獲取單例 bean
2018-06-04 Spring IOC 容器源碼分析 - 建立單例 bean 的過程
2018-06-06 Spring IOC 容器源碼分析 - 建立原始 bean 對象
2018-06-08 Spring IOC 容器源碼分析 - 循環依賴的解決辦法
2018-06-11 Spring IOC 容器源碼分析 - 填充屬性到 bean 原始對象
2018-06-11 Spring IOC 容器源碼分析 - 餘下的初始化工做

Ⅱ. AOP

更新時間 標題
2018-06-17 Spring AOP 源碼分析系列文章導讀
2018-06-20 Spring AOP 源碼分析 - 篩選合適的通知器
2018-06-20 Spring AOP 源碼分析 - 建立代理對象
2018-06-22 Spring AOP 源碼分析 - 攔截器鏈的執行過程

Ⅲ. MVC

更新時間 標題
2018-06-29 Spring MVC 原理探祕 - 一個請求的旅行過程
2018-06-30 Spring MVC 原理探祕 - 容器的建立過程

本文在知識共享許可協議 4.0 下發布,轉載需在明顯位置處註明出處
做者:田小波
本文同步發佈在個人我的博客:http://www.tianxiaobo.com

cc
本做品採用知識共享署名-非商業性使用-禁止演繹 4.0 國際許可協議進行許可。

相關文章
相關標籤/搜索