亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

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

SKYZACK / 1513人閱讀

摘要:源碼分析源碼一覽本節(jié),我們先來看一下填充屬性的方法,即。所有的屬性值是在方法中統(tǒng)一被注入到對(duì)象中的。檢測是否存在與相關(guān)的或。這樣可以在很大程度上降低源碼分析的難度。若候選項(xiàng)是非類型,則表明已經(jīng)完成了實(shí)例化,此時(shí)直接返回即可。

1. 簡介

本篇文章,我們來一起了解一下 Spring 是如何將配置文件中的屬性值填充到 bean 對(duì)象中的。我在前面幾篇文章中介紹過 Spring 創(chuàng)建 bean 的流程,即 Spring 先通過反射創(chuàng)建一個(gè)原始的 bean 對(duì)象,然后再向這個(gè)原始的 bean 對(duì)象中填充屬性。對(duì)于填充屬性這個(gè)過程,簡單點(diǎn)來說,JavaBean 的每個(gè)屬性通常都有 getter/setter 方法,我們可以直接調(diào)用 setter 方法將屬性值設(shè)置進(jìn)去。當(dāng)然,這樣做還是太簡單了,填充屬性的過程中還有許多事情要做。比如在 Spring 配置中,所有屬性值都是以字符串的形式進(jìn)行配置的,我們?cè)趯⑦@些屬性值賦值給對(duì)象的成員變量時(shí),要根據(jù)變量類型進(jìn)行相應(yīng)的類型轉(zhuǎn)換。對(duì)于一些集合類的配置,比如 、,還要將這些配置轉(zhuǎn)換成相應(yīng)的集合對(duì)象才能進(jìn)行后續(xù)的操作。除此之外,如果用戶配置了自動(dòng)注入(autowire = byName/byType),Spring 還要去為自動(dòng)注入的屬性尋找合適的注入項(xiàng)。由此可以見,屬性填充的整個(gè)過程還是很復(fù)雜的,并非是簡單調(diào)用 setter 方法設(shè)置屬性值即可。

關(guān)于屬性填充的一些知識(shí),本章先介紹這里。接下來,我們深入到源碼中,從源碼中了解屬性填充的整個(gè)過程。

2. 源碼分析 2.1 populateBean 源碼一覽

本節(jié),我們先來看一下填充屬性的方法,即 populateBean。該方法并不復(fù)雜,但它所調(diào)用的一些方法比較復(fù)雜。不過好在我們這里只需要知道這些方法都有什么用就行了,暫時(shí)不用糾結(jié)細(xì)節(jié)。好了,下面看源碼吧。

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 類型的后置處理器一個(gè)修改 
     * bean 狀態(tài)的機(jī)會(huì)。關(guān)于這段后置引用,官方的解釋是:讓用戶可以自定義屬性注入。比如用戶實(shí)現(xiàn)一
     * 個(gè) InstantiationAwareBeanPostProcessor 類型的后置處理器,并通過 
     * postProcessAfterInstantiation 方法向 bean 的成員變量注入自定義的信息。當(dāng)然,如果無
     * 特殊需求,直接使用配置中的信息注入即可。另外,Spring 并不建議大家直接實(shí)現(xiàn) 
     * InstantiationAwareBeanPostProcessor 接口,如果想實(shí)現(xiàn)這種類型的后置處理器,更建議
     * 通過繼承 InstantiationAwareBeanPostProcessorAdapter 抽象類實(shí)現(xiàn)自定義后置處理器。
     */
    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;
                }
            }
        }
    }

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

    // 根據(jù)名稱或類型注入依賴
    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 對(duì)象前,對(duì)屬性的值進(jìn)行相應(yīng)的處理,
     * 比如可以修改某些屬性的值。這時(shí)注入到 bean 中的值就不是配置文件中的內(nèi)容了,
     * 而是經(jīng)過后置處理器修改后的內(nèi)容
     */ 
    if (hasInstAwareBpps || needsDepCheck) {
        PropertyDescriptor[] filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
        if (hasInstAwareBpps) {
            for (BeanPostProcessor bp : getBeanPostProcessors()) {
                if (bp instanceof InstantiationAwareBeanPostProcessor) {
                    InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
                    // 對(duì)屬性進(jìn)行后置處理
                    pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
                    if (pvs == null) {
                        return;
                    }
                }
            }
        }
        if (needsDepCheck) {
            checkDependencies(beanName, mbd, filteredPds, pvs);
        }
    }

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

上面的源碼注釋的比較詳細(xì)了,下面我們來總結(jié)一下這個(gè)方法的執(zhí)行流程。如下:

獲取屬性列表 pvs

在屬性被填充到 bean 前,應(yīng)用后置處理自定義屬性填充

根據(jù)名稱或類型解析相關(guān)依賴

再次應(yīng)用后置處理,用于動(dòng)態(tài)修改屬性列表 pvs 的內(nèi)容

將屬性應(yīng)用到 bean 對(duì)象中

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

在下面的章節(jié)中,我將會(huì)對(duì) populateBean 方法中比較重要的幾個(gè)方法調(diào)用進(jìn)行分析,也就是第3步和第5步中的三個(gè)方法。好了,本節(jié)先到這里。

2.2 autowireByName 方法分析

本節(jié)來分析一下 autowireByName 方法的代碼,其實(shí)這個(gè)方法根據(jù)方法名,大家應(yīng)該知道它有什么用了。所以我也就不啰嗦了,咱們直奔主題,直接分析源碼:

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

    /*
     * 獲取非簡單類型屬性的名稱,且該屬性未被配置在配置文件中。這里從反面解釋一下什么是"非簡單類型"
     * 屬性,我們先來看看 Spring 認(rèn)為的"簡單類型"屬性有哪些,如下:
     *   1. CharSequence 接口的實(shí)現(xiàn)類,比如 String
     *   2. Enum
     *   3. Date
     *   4. URI/URL
     *   5. Number 的繼承類,比如 Integer/Long
     *   6. byte/short/int... 等基本類型
     *   7. Locale
     *   8. 以上所有類型的數(shù)組形式,比如 String[]、Date[]、int[] 等等
     * 
     * 除了要求非簡單類型的屬性外,還要求屬性未在配置文件中配置過,也就是 pvs.contains(pd.getName()) = false。
     */
    String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
    for (String propertyName : propertyNames) {
        // 檢測是否存在與 propertyName 相關(guān)的 bean 或 BeanDefinition。若存在,則調(diào)用 BeanFactory.getBean 方法獲取 bean 實(shí)例
        if (containsBean(propertyName)) {
            // 從容器中獲取相應(yīng)的 bean 實(shí)例
            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 方法的邏輯比較簡單,該方法首先獲取非簡單類型屬性的名稱,然后再根據(jù)名稱到容器中獲取相應(yīng)的 bean 實(shí)例,最后再將獲取到的 bean 添加到屬性列表中即可。既然這個(gè)方法比較簡單,那我也就不多說了,繼續(xù)下面的分析。

2.3 autowireByType 方法分析

本節(jié)我們來分析一下 autowireByName 的孿生兄弟 autowireByType,相較于 autowireByName,autowireByType 則要復(fù)雜一些,復(fù)雜之處在于解析依賴的過程。不過也沒關(guān)系,如果我們不過于糾結(jié)細(xì)節(jié),我們完全可以把一些復(fù)雜的地方當(dāng)做一個(gè)黑盒,我們只需要要知道這個(gè)黑盒有什么用即可。這樣可以在很大程度上降低源碼分析的難度。好了,其他的就不多說了,咱們來分析源碼吧。

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

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

    Set autowiredBeanNames = new LinkedHashSet(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)的參數(shù)信息,比如參數(shù)在參數(shù)列表中的
                 * 位置,參數(shù)類型,以及該參數(shù)所歸屬的方法等信息
                 */
                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());
                // 創(chuàng)建依賴描述對(duì)象
                DependencyDescriptor desc = new AutowireByTypeDependencyDescriptor(methodParam, eager);
                /*
                 * 下面的方法用于解析依賴。過程比較復(fù)雜,先把這里看成一個(gè)黑盒,我們只要知道這
                 * 個(gè)方法可以幫我們解析出合適的依賴即可。
                 */
                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 的代碼本身并不復(fù)雜。和 autowireByName 一樣,autowireByType 首先也是獲取非簡單類型屬性的名稱。然后再根據(jù)屬性名獲取屬性描述符,并由屬性描述符獲取方法參數(shù)對(duì)象 MethodParameter,隨后再根據(jù) MethodParameter 對(duì)象獲取依賴描述符對(duì)象,整個(gè)過程為 beanName → PropertyDescriptor → MethodParameter → DependencyDescriptor。在獲取到依賴描述符對(duì)象后,再根據(jù)依賴描述符解析出合適的依賴。最后將解析出的結(jié)果存入屬性列表 pvs 中即可。

關(guān)于 autowireByType 方法中出現(xiàn)的幾種描述符對(duì)象,大家自己去看一下他們的實(shí)現(xiàn)吧,我就不分析了。接下來,我們來分析一下解析依賴的方法 resolveDependency。如下:

public Object resolveDependency(DependencyDescriptor descriptor, String requestingBeanName,
        Set 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 autowiredBeanNames, TypeConverter typeConverter) throws BeansException {

    InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor);
    try {
        // 該方法最終調(diào)用了 beanFactory.getBean(String, Class),從容器中獲取依賴
        Object shortcut = descriptor.resolveShortcut(this);
        // 如果容器中存在所需依賴,這里進(jìn)行斷路操作,提前結(jié)束依賴解析邏輯
        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()));
        }

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

        /*
         * 按類型查找候選列表,如果某個(gè)類型已經(jīng)被實(shí)例化,則返回相應(yīng)的實(shí)例。
         * 比如下面的配置:
         *
         *   
         *   
         *   
         *
         * MongoDao 和 MySqlDao 均實(shí)現(xiàn)自 Dao 接口,Service 對(duì)象(不是接口)中有一個(gè) Dao 
         * 類型的屬性?,F(xiàn)在根據(jù)類型自動(dòng)注入 Dao 的實(shí)現(xiàn)類。這里有兩個(gè)候選 bean,一個(gè)是 
         * mongoDao,另一個(gè)是 mysqlDao,其中 mongoDao 在 service 之前實(shí)例化,
         * mysqlDao 在 service 之后實(shí)例化。此時(shí) findAutowireCandidates 方法會(huì)返回如下的結(jié)果:
         *
         *   matchingBeans = [ ,  ]
         *
         * 注意 mysqlDao 還未實(shí)例化,所以返回的是 MySqlDao.class。
         * 
         * findAutowireCandidates 這個(gè)方法邏輯比較復(fù)雜,我簡單說一下它的工作流程吧,如下:
         *   1. 從 BeanFactory 中獲取某種類型 bean 的名稱,比如上面的配置中 
         *      mongoDao 和 mysqlDao 均實(shí)現(xiàn)了 Dao 接口,所以他們是同一種類型的 bean。
         *   2. 遍歷上一步得到的名稱列表,并判斷 bean 名稱對(duì)應(yīng)的 bean 是否是合適的候選項(xiàng),
         *      若合適則添加到候選列表中,并在最后返回候選列表
         *      
         * findAutowireCandidates 比較復(fù)雜,我并未完全搞懂,就不深入分析了。見諒
         */
        Map 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,則表明存在多個(gè)可注入的候選項(xiàng),這里判斷使用哪一個(gè)
             * 候選項(xiàng)。比如下面的配置:
             *
             *   
             *   
             *
             * mongoDao 的配置中存在 primary 屬性,所以 mongoDao 會(huì)被選為最終的候選項(xiàng)。如
             * 果兩個(gè) bean 配置都沒有 primary 屬性,則需要根據(jù)優(yōu)先級(jí)選擇候選項(xiàng)。優(yōu)先級(jí)這一塊
             * 的邏輯沒細(xì)看,不多說了。
             */
            autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
            if (autowiredBeanName == null) {
                if (isRequired(descriptor) || !indicatesMultipleBeans(type)) {
                    // 拋出 NoUniqueBeanDefinitionException 異常
                    return descriptor.resolveNotUnique(type, matchingBeans);
                }
                else {
                    return null;
                }
            }
            // 根據(jù)解析出的 autowiredBeanName,獲取相應(yīng)的候選項(xiàng)
            instanceCandidate = matchingBeans.get(autowiredBeanName);
        }
        else { // 只有一個(gè)候選項(xiàng),直接取出來即可
            Map.Entry entry = matchingBeans.entrySet().iterator().next();
            autowiredBeanName = entry.getKey();
            instanceCandidate = entry.getValue();
        }

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

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

由上面的代碼可以看出,doResolveDependency 這個(gè)方法還是挺復(fù)雜的。這里我就不繼續(xù)分析 doResolveDependency 所調(diào)用的方法了,對(duì)于這些方法,我也是似懂非懂。好了,本節(jié)的最后我們來總結(jié)一下 doResolveDependency 的執(zhí)行流程吧,如下:

首先將 beanName 和 requiredType 作為參數(shù),并嘗試從 BeanFactory 中獲取與此對(duì)于的 bean。若獲取成功,就可以提前結(jié)束 doResolveDependency 的邏輯。

處理 @value 注解

解析數(shù)組、List、Map 等類型的依賴,如果解析結(jié)果不為空,則返回結(jié)果

根據(jù)類型查找合適的候選項(xiàng)

如果候選項(xiàng)的數(shù)量為0,則拋出異常。為1,直接從候選列表中取出即可。若候選項(xiàng)數(shù)量 > 1,則在多個(gè)候選項(xiàng)中確定最優(yōu)候選項(xiàng),若無法確定則拋出異常

若候選項(xiàng)是 Class 類型,表明候選項(xiàng)還沒實(shí)例化,此時(shí)通過 BeanFactory.getBean 方法對(duì)其進(jìn)行實(shí)例化。若候選項(xiàng)是非 Class 類型,則表明已經(jīng)完成了實(shí)例化,此時(shí)直接返回即可。

好了,本節(jié)的內(nèi)容先到這里。如果有分析錯(cuò)的地方,歡迎大家指出來。

2.4 applyPropertyValues 方法分析

經(jīng)過了上面的流程,現(xiàn)在終于可以將屬性值注入到 bean 對(duì)象中了。當(dāng)然,這里還不能立即將屬性值注入到對(duì)象中,因?yàn)樵?Spring 配置文件中屬性值都是以 String 類型進(jìn)行配置的,所以 Spring 框架需要對(duì) String 類型進(jìn)行轉(zhuǎn)換。除此之外,對(duì)于 ref 屬性,這里還需要根據(jù) 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 original;

    if (pvs instanceof MutablePropertyValues) {
        mpvs = (MutablePropertyValues) pvs;
        // 如果屬性列表 pvs 被轉(zhuǎn)換過,則直接返回即可
        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 deepCopy = new ArrayList(original.size());
    boolean resolveNecessary = false;
    // 遍歷屬性列表
    for (PropertyValue pv : original) {
        // 如果屬性值被轉(zhuǎn)換過,則就不需要再次轉(zhuǎn)換
        if (pv.isConverted()) {
            deepCopy.add(pv);
        }
        else {
            String propertyName = pv.getName();
            Object originalValue = pv.getValue();
            /*
             * 解析屬性值。舉例說明,先看下面的配置:
             * 
             *   
             *       
             *       
             *       
             *       
             *           
             *               USB
             *               HDMI
             *               Thunderbolt
             *           
             *       
             *   
             *
             * 上面是一款電腦的配置信息,每個(gè) property 配置經(jīng)過下面的方法解析后,返回如下結(jié)果:
             *   propertyName = "manufacturer", resolvedValue = "Apple"
             *   propertyName = "width", resolvedValue = "280"
             *   propertyName = "cpu", resolvedValue = "CPU@1234"  注:resolvedValue 是一個(gè)對(duì)象
             *   propertyName = "interface", resolvedValue = ["USB", "HDMI", "Thunderbolt"]
             *
             * 如上所示,resolveValueIfNecessary 會(huì)將 ref 解析為具體的對(duì)象,將  
             * 標(biāo)簽轉(zhuǎn)換為 List 對(duì)象等。對(duì)于 int 類型的配置,這里并未做轉(zhuǎn)換,所以 
             * width = "280",還是字符串。除了解析上面幾種類型,該方法還會(huì)解析 、
             * 、 等集合配置
             */
            Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
            Object convertedValue = resolvedValue;

            /*
             * convertible 表示屬性值是否可轉(zhuǎn)換,由兩個(gè)條件合成而來。第一個(gè)條件不難理解,解釋
             * 一下第二個(gè)條件。第二個(gè)條件用于檢測 propertyName 是否是 nested 或者 indexed,
             * 直接舉例說明吧:
             * 
             *   public class Room {
             *       private Door door = new Door();
             *   }
             *
             * room 對(duì)象里面包含了 door 對(duì)象,如果我們想向 door 對(duì)象中注入屬性值,則可以這樣配置:
             *
             *   
             *      
             *   
             * 
             * isNestedOrIndexedProperty 會(huì)根據(jù) propertyName 中是否包含 . 或 [  返回 
             * true 和 false。包含則返回 true,否則返回 false。關(guān)于 nested 類型的屬性,我
             * 沒在實(shí)踐中用過,所以不知道上面舉的例子是不是合理。若不合理,歡迎指正,也請(qǐng)多多指教。
             * 關(guān)于 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);
            // 對(duì)于一般的屬性,convertible 通常為 true
            if (convertible) {
                // 對(duì)屬性值的類型進(jìn)行轉(zhuǎn)換,比如將 String 類型的屬性值 "123" 轉(zhuǎn)為 Integer 類型的 123
                convertedValue = convertForProperty(resolvedValue, propertyName, bw, converter);
            }

            /*
             * 如果 originalValue 是通過 autowireByType 或 autowireByName 解析而來,
             * 那么此處條件成立,即 (resolvedValue == originalValue) = true
             */
            if (resolvedValue == originalValue) {
                if (convertible) {
                    // 將 convertedValue 設(shè)置到 pv 中,后續(xù)再次創(chuàng)建同一個(gè) bean 時(shí),就無需再次進(jìn)行轉(zhuǎn)換了
                    pv.setConvertedValue(convertedValue);
                }
                deepCopy.add(pv);
            }
            /*
             * 如果原始值 originalValue 是 TypedStringValue,且轉(zhuǎn)換后的值 
             * convertedValue 不是 Collection 或數(shù)組類型,則將轉(zhuǎn)換后的值存入到 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 {
        // 將所有的屬性值設(shè)置到 bean 實(shí)例中
        bw.setPropertyValues(new MutablePropertyValues(deepCopy));
    }
    catch (BeansException ex) {
        throw new BeanCreationException(
                mbd.getResourceDescription(), beanName, "Error setting property values", ex);
    }
}

以上就是 applyPropertyValues 方法的源碼,配合著我寫的注釋,應(yīng)該可以理解這個(gè)方法的流程。這個(gè)方法也調(diào)用了很多其他的方法,如果大家跟下去的話,會(huì)發(fā)現(xiàn)這些方法的調(diào)用棧也是很深的,比較復(fù)雜。這里說一下 bw.setPropertyValues 這個(gè)方法,如果大家跟到這個(gè)方法的調(diào)用棧的最底部,會(huì)發(fā)現(xiàn)這個(gè)方法是通過調(diào)用對(duì)象的 setter 方法進(jìn)行屬性設(shè)置的。這里貼一下簡化后的代碼:

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;
            // 調(diào)用 setter 方法,getWrappedInstance() 返回的是 bean 對(duì)象
            writeMethod.invoke(getWrappedInstance(), value);
        }
    }
}

好了,本節(jié)的最后來總結(jié)一下 applyPropertyValues 方法的執(zhí)行流程吧,如下:

檢測屬性值列表是否已轉(zhuǎn)換過的,若轉(zhuǎn)換過,則直接填充屬性,無需再次轉(zhuǎn)換

遍歷屬性值列表 pvs,解析原始值 originalValue,得到解析值 resolvedValue

對(duì)解析后的屬性值 resolvedValue 進(jìn)行類型轉(zhuǎn)換

將類型轉(zhuǎn)換后的屬性值設(shè)置到 PropertyValue 對(duì)象中,并將 PropertyValue 對(duì)象存入 deepCopy 集合中

將 deepCopy 中的屬性信息注入到 bean 對(duì)象中

3. 總結(jié)

本文對(duì) populateBean 方法及其所調(diào)用的 autowireByName、autowireByType 和 applyPropertyValues 做了較為詳細(xì)的分析,不知道大家看完后感覺如何。我說一下我的感受吧,從我看 Spring IOC 部分的源碼到現(xiàn)在寫了5篇關(guān)于 IOC 部分的源碼分析文章,總體感覺 Spring 的源碼還是很復(fù)雜的,調(diào)用層次很深。如果想對(duì)源碼有一個(gè)比較好的理解,需要不少的時(shí)間去分析,調(diào)試源碼??偟膩碚f,不容易。當(dāng)然,我的水平有限。如果大家自己去閱讀源碼,可能會(huì)覺得也沒這么難啊。

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

本文在知識(shí)共享許可協(xié)議 4.0 下發(fā)布,轉(zhuǎn)載需在明顯位置處注明出處
作者:coolblog.xyz
本文同步發(fā)布在我的個(gè)人博客:http://www.coolblog.xyz
附錄:Spring 源碼分析文章列表 Ⅰ. IOC
更新時(shí)間 標(biāo)題
2018-05-30 Spring IOC 容器源碼分析系列文章導(dǎo)讀
2018-06-01 Spring IOC 容器源碼分析 - 獲取單例 bean
2018-06-04 Spring IOC 容器源碼分析 - 創(chuàng)建單例 bean 的過程
2018-06-06 Spring IOC 容器源碼分析 - 創(chuàng)建原始 bean 對(duì)象
2018-06-08 Spring IOC 容器源碼分析 - 循環(huán)依賴的解決辦法
2018-06-11 Spring IOC 容器源碼分析 - 填充屬性到 bean 原始對(duì)象
2018-06-11 Spring IOC 容器源碼分析 - 余下的初始化工作
Ⅱ. AOP
更新時(shí)間 標(biāo)題
2018-06-17 Spring AOP 源碼分析系列文章導(dǎo)讀
2018-06-20 Spring AOP 源碼分析 - 篩選合適的通知器
2018-06-20 Spring AOP 源碼分析 - 創(chuàng)建代理對(duì)象
2018-06-22 Spring AOP 源碼分析 - 攔截器鏈的執(zhí)行過程
Ⅲ. MVC
更新時(shí)間 標(biāo)題
2018-06-29 Spring MVC 原理探秘 - 一個(gè)請(qǐng)求的旅行過程
2018-06-30 Spring MVC 原理探秘 - 容器的創(chuàng)建過程


本作品采用知識(shí)共享署名-非商業(yè)性使用-禁止演繹 4.0 國際許可協(xié)議進(jìn)行許可。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/69718.html

Failed to recv the data from server completely (SIZE:0/8, REASON:closed)