1. 程式人生 > >Spring IOC 容器源碼分析 - 填充屬性到 bean 原始對象

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

interface let 源碼分析 添加 eat object determine 方法調用 集合類

1. 簡介

本篇文章,我們來一起了解一下 Spring 是如何將配置文件中的屬性值填充到 bean 對象中的。我在前面幾篇文章中介紹過 Spring 創建 bean 的流程,即 Spring 先通過反射創建一個原始的 bean 對象,然後再向這個原始的 bean 對象中填充屬性。對於填充屬性這個過程,簡單點來說,JavaBean 的每個屬性通常都有 getter/setter 方法,我們可以直接調用 setter 方法將屬性值設置進去。當然,這樣做還是太簡單了,填充屬性的過程中還有許多事情要做。比如在 Spring 配置中,所有屬性值都是以字符串的形式進行配置的,我們在將這些屬性值賦值給對象的成員變量時,要根據變量類型進行相應的類型轉換。對於一些集合類的配置,比如

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

2. 源碼分析

2.1 populateBean 源碼一覽

本節,我們先來看一下填充屬性的方法,即 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); }

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

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

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

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

2.2 autowireByName 方法分析

本節來分析一下 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");
            }
        }
    }
}

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

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 中即可。

關於 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 的源碼還是很復雜的,調用層次很深。如果想對源碼有一個比較好的理解,需要不少的時間去分析,調試源碼。總的來說,不容易。當然,我的水平有限。如果大家自己去閱讀源碼,可能會覺得也沒這麽難啊。

好了,其他的就不多說了。如果本文中有分析錯的地方,歡迎大家指正。最後感謝大家的閱讀。

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

技術分享圖片
本作品采用知識共享署名-非商業性使用-禁止演繹 4.0 國際許可協議進行許可。

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