spring源碼日記15: 屬性填充

所有文章已遷移至csdn腹侣,csdn個(gè)人主頁(yè)https://blog.csdn.net/chaitoudaren

spring屬性注入分4種:

  1. 不開(kāi)啟自動(dòng)注入,即xml自己配置property
  2. 通過(guò)名稱自動(dòng)注入
  3. 通過(guò)類型自動(dòng)注入
  4. @Autowire自動(dòng)注入

本篇我們將介紹前三種磕道,@Autowire應(yīng)該是百分之99的開(kāi)發(fā)者選擇的的注入方式,它通過(guò)屬性填充中的后置處理器完成,因此本篇有涉及后置處理器的不用太糾結(jié)枪孩,將單獨(dú)一片詳解:spring源碼日期16: @Autowired實(shí)現(xiàn)原理

下方代碼,本篇只關(guān)心第4點(diǎn)屬性填充

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
            throws BeanCreationException {
        ...
        try {
            //
            /**
             * 4. 填充屬性
             * 如果@Autowired注解屬性藻肄,則在上方完成解析后蔑舞,在這里完成注入
             *
             * @Autowired
             * private Inner inner;
             */
            populateBean(beanName, mbd, instanceWrapper);
            // 5. 初始化
            exposedObject = initializeBean(beanName, exposedObject, mbd);
        }
        ...
}

屬性填充

    protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {

        // 如果BeanWrapper對(duì)象為null
        if (bw == null) {
            if (mbd.hasPropertyValues()) {
                // 有屬性但是沒(méi)對(duì)象,那往哪里注入...直接拋異常了
                throw new BeanCreationException(
                        mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance");
            }
            else {
                // Skip property population phase for null instance.
                // 沒(méi)有屬性就直接返回
                return;
            }
        }

        // Give any InstantiationAwareBeanPostProcessors the opportunity to modify the
        // state of the bean before properties are set. This can be used, for example,
        // to support styles of field injection.
        // 1. 實(shí)例化后的后置操作
        if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
            for (BeanPostProcessor bp : getBeanPostProcessors()) {
                if (bp instanceof InstantiationAwareBeanPostProcessor) {
                    InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
                    if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
                        // 實(shí)例化后置處理器可以發(fā)出終止填充的命令嘹屯,這點(diǎn)比較特別
                        return;
                    }
                }
            }
        }
        // 2. 獲取屬性值
        PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);

        // 3. 根據(jù)注入方式的不同可分為 通過(guò) ①名稱注入攻询、②類型注入、③不自動(dòng)注入
        /**
         * ①按名稱注入州弟,則pvs在步驟2時(shí)會(huì)獲取到配置文件中配置注入的屬性钧栖,同時(shí)在autowireByName又會(huì)進(jìn)一步通過(guò)名稱添加補(bǔ)充
         * ②按名稱注入,則pvs在步驟2時(shí)會(huì)獲取到配置文件中配置注入的屬性婆翔,同時(shí)在autowireByType又會(huì)進(jìn)一步通過(guò)類型添加補(bǔ)充
         * ③不開(kāi)啟自動(dòng)注入拯杠,則在步驟2 pvs就可以獲取到配置文件中注入的屬性
         */
        int resolvedAutowireMode = mbd.getResolvedAutowireMode();
        if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
            MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
            // Add property values based on autowire by name if applicable.
            if (resolvedAutowireMode == AUTOWIRE_BY_NAME) {
                // ①通過(guò)名稱自動(dòng)注入
                autowireByName(beanName, mbd, bw, newPvs);
            }
            // Add property values based on autowire by type if applicable.
            if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
                // ②通過(guò)類型自動(dòng)注入
                autowireByType(beanName, mbd, bw, newPvs);
            }
            pvs = newPvs;
        }

        // 判斷所有實(shí)例化后置處理器是否都已經(jīng)初始化完成
        boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
        // 依賴檢測(cè)
        boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE);

        PropertyDescriptor[] filteredPds = null;
        if (hasInstAwareBpps) {
            if (pvs == null) {
                pvs = mbd.getPropertyValues();
            }
            /**
             * 4. 屬性填充的后置處理器
             * @Autowired
             * private Inner inner;
             * 在這里使用AutowiredAnnotationBeanPostProcessor進(jìn)行注入
             */
            for (BeanPostProcessor bp : getBeanPostProcessors()) {
                if (bp instanceof InstantiationAwareBeanPostProcessor) {
                    InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
                    // @Autowired正是在這里調(diào)用后置處理器對(duì)屬性進(jìn)行注入
                    PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
                    if (pvsToUse == null) {
                        if (filteredPds == null) {
                            filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
                        }
                        // 對(duì)所有需要檢測(cè)的屬性進(jìn)行后置處理
                        pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
                        if (pvsToUse == null) {
                            return;
                        }
                    }
                    pvs = pvsToUse;
                }
            }
        }
        if (needsDepCheck) {
            if (filteredPds == null) {
                filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
            }
            checkDependencies(beanName, mbd, filteredPds, pvs);
        }

        if (pvs != null) {
            // 5. 將屬性應(yīng)用到bean中
            applyPropertyValues(beanName, mbd, bw, pvs);
        }
    }
  1. 實(shí)例化后置處理
  2. 獲取屬性值
    這邊獲取到的pvs是從配置文件中獲取的,代碼如下啃奴,xml配置文件中property標(biāo)簽僅包含b1潭陪,所以pvs僅僅會(huì)獲取到b1,如果沒(méi)有下一步的代碼將不會(huì)注入b2(這點(diǎn)很容易被忽視,一定引起重視)
public Class A {
    private B1 b1;
    private B2 b2;
    // getter&&setter
}
public Class B1 {}
public Class B2 {}

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"
       default-autowire="byName"
>
    <bean id="a" class="autowire.A">
        <property name="b1" ref="b1"/>
    </bean>

    <bean id="b1" class="B1"/>
    <bean id="b2" class="B2"/>
</beans>
  1. 自動(dòng)注入(其實(shí)這里只是獲取待注入屬性依溯,并未開(kāi)始實(shí)際的注入操作)
    還是上方代碼老厌,注意一定要加上default-autowire="byName"這句,這句指開(kāi)啟按名稱注入黎炉,否則spring默認(rèn)不開(kāi)啟自動(dòng)注入枝秤。開(kāi)啟后,spring將通過(guò)Class A類中的屬性拜隧,按名稱b2通過(guò)getBean("b2")的方式進(jìn)行查找宿百,并將它添加到pvs中,至此屬性b1, b2才都被成功添加到pvs中洪添,等待注入
    protected void autowireByName(
            String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {
        // 獲取pvs以外的其他屬性垦页,該屬性必須有setter方法
        String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
        for (String propertyName : propertyNames) {
            if (containsBean(propertyName)) {
                // 直接通過(guò)屬性名獲取bean,并假如到pvs中
                Object bean = getBean(propertyName);
                pvs.add(propertyName, bean);
                registerDependentBean(propertyName, beanName);
                if (logger.isTraceEnabled()) {
                    logger.trace("Added autowiring by name from bean name '" + beanName +
                            "' via property '" + propertyName + "' to bean named '" + propertyName + "'");
                }
            }
            else {
                if (logger.isTraceEnabled()) {
                    logger.trace("Not autowiring property '" + propertyName + "' of bean '" + beanName +
                            "' by name: no matching bean found");
                }
            }
        }
    }
  1. 屬性填充的后置處理器干奢,@Autowire正是在這里注入的痊焊,我們將單獨(dú)一章詳解:spring源碼日期16: @Autowired實(shí)現(xiàn)原理
  2. 將屬性注入到bean中
    代碼很大一部分工作用在了屬性轉(zhuǎn)換上,例如property傳入String類型而實(shí)際屬性則為int類型忿峻,當(dāng)然這是最簡(jiǎn)單的一種薄啥。而我們最關(guān)心的注入操作則發(fā)生在第6步,具體代碼很長(zhǎng)就不詳細(xì)分析了逛尚,但是最核心的操作則是通過(guò)反射獲取到對(duì)應(yīng)屬性field垄惧,然后調(diào)用field.set(bean, property);實(shí)現(xiàn)對(duì)bean屬性的賦值
    protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) {
        // 1. pvs為空就沒(méi)得注入了
        if (pvs.isEmpty()) {
            return;
        }

        // 2. 權(quán)限管理器
        if (System.getSecurityManager() != null && bw instanceof BeanWrapperImpl) {
            ((BeanWrapperImpl) bw).setSecurityContext(getAccessControlContext());
        }

        //MutablePropertyValues是PropertyValues接口的默認(rèn)實(shí)現(xiàn)類
        MutablePropertyValues mpvs = null;
        List<PropertyValue> original;

        // 3. 獲取bean的屬性集合
        if (pvs instanceof MutablePropertyValues) {
            mpvs = (MutablePropertyValues) pvs;
            // pvs都需要被轉(zhuǎn)換成對(duì)應(yīng)的類型才可以應(yīng)用到beanwapper中
            if (mpvs.isConverted()) {
                // Shortcut: use the pre-converted values as-is.
                // 如果pvs已經(jīng)轉(zhuǎn)換過(guò),則直接設(shè)置屬性值無(wú)需再次轉(zhuǎn)換
                try {
                    bw.setPropertyValues(mpvs);
                    return;
                }
                catch (BeansException ex) {
                    throw new BeanCreationException(
                            mbd.getResourceDescription(), beanName, "Error setting property values", ex);
                }
            }
            // 否則獲取原始PropertyValue集合
            original = mpvs.getPropertyValueList();
        }
        else {
            original = Arrays.asList(pvs.getPropertyValues());
        }

        // 4. 獲取類型轉(zhuǎn)換器
        TypeConverter converter = getCustomTypeConverter();
        if (converter == null) {
            converter = bw;
        }
        BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this, beanName, mbd, converter);

        // Create a deep copy, resolving any references for values.
        // 5. 通過(guò)深度拷貝,解析值引用
        List<PropertyValue> deepCopy = new ArrayList<>(original.size());
        boolean resolveNecessary = false;
        // 循環(huán)轉(zhuǎn)換PropertyValues
        for (PropertyValue pv : original) {
            // 已經(jīng)轉(zhuǎn)換過(guò),則直接加入deepCopy
            if (pv.isConverted()) {
                deepCopy.add(pv);
            }
            else {
                // 屬性名
                String propertyName = pv.getName();
                // 屬性值
                Object originalValue = pv.getValue();
                if (originalValue == AutowiredPropertyMarker.INSTANCE) {
                    Method writeMethod = bw.getPropertyDescriptor(propertyName).getWriteMethod();
                    if (writeMethod == null) {
                        throw new IllegalArgumentException("Autowire marker for property without write method: " + pv);
                    }
                    originalValue = new DependencyDescriptor(new MethodParameter(writeMethod, 0), true);
                }

                // 解析原始屬性值
                // 當(dāng)注入集合屬性時(shí),如果指定了value-type,如value-type="java.lang.String",那么resolveValueIfNecessary會(huì)執(zhí)行類型的轉(zhuǎn)換操作
                Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
                Object convertedValue = resolvedValue;

                // isWritableProperty判斷屬性是否可寫(xiě)绰寞,isNestedOrIndexedProperty判斷是否索引屬性或者嵌套屬性
                boolean convertible = bw.isWritableProperty(propertyName) &&
                        !PropertyAccessorUtils.isNestedOrIndexedProperty(propertyName);
                // 是否轉(zhuǎn)換
                if (convertible) {
                    // 類型轉(zhuǎn)換
                    convertedValue = convertForProperty(resolvedValue, propertyName, bw, converter);
                }
                // Possibly store converted value in merged bean definition,
                // in order to avoid re-conversion for every created bean instance.
                // 緩存已經(jīng)轉(zhuǎn)換過(guò)的值,避免再次轉(zhuǎn)換到逊。這取決于convertForProperty是否真正對(duì)屬性進(jìn)行了轉(zhuǎn)換
                if (resolvedValue == originalValue) {
                    if (convertible) {
                        pv.setConvertedValue(convertedValue);
                    }
                    deepCopy.add(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();
        }

        // Set our (possibly massaged) deep copy.
        try {
            // 6. 設(shè)置屬性值,實(shí)質(zhì)就是通過(guò)反射調(diào)用對(duì)屬性進(jìn)行賦值
            bw.setPropertyValues(new MutablePropertyValues(deepCopy));
        }
        catch (BeansException ex) {
            throw new BeanCreationException(
                    mbd.getResourceDescription(), beanName, "Error setting property values", ex);
        }
    }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末滤钱,一起剝皮案震驚了整個(gè)濱河市觉壶,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌件缸,老刑警劉巖铜靶,帶你破解...
    沈念sama閱讀 222,104評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異他炊,居然都是意外死亡争剿,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)佑稠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)秒梅,“玉大人,你說(shuō)我怎么就攤上這事舌胶±κ瘢” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,697評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)辆它。 經(jīng)常有香客問(wèn)我誊薄,道長(zhǎng),這世上最難降的妖魔是什么锰茉? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,836評(píng)論 1 298
  • 正文 為了忘掉前任呢蔫,我火速辦了婚禮,結(jié)果婚禮上飒筑,老公的妹妹穿的比我還像新娘片吊。我一直安慰自己,他們只是感情好协屡,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,851評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布俏脊。 她就那樣靜靜地躺著,像睡著了一般肤晓。 火紅的嫁衣襯著肌膚如雪爷贫。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 52,441評(píng)論 1 310
  • 那天补憾,我揣著相機(jī)與錄音漫萄,去河邊找鬼。 笑死盈匾,一個(gè)胖子當(dāng)著我的面吹牛腾务,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播削饵,決...
    沈念sama閱讀 40,992評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼窑睁,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了葵孤?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,899評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤橱赠,失蹤者是張志新(化名)和其女友劉穎尤仍,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體狭姨,經(jīng)...
    沈念sama閱讀 46,457評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡宰啦,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,529評(píng)論 3 341
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了饼拍。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片赡模。...
    茶點(diǎn)故事閱讀 40,664評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖师抄,靈堂內(nèi)的尸體忽然破棺而出漓柑,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 36,346評(píng)論 5 350
  • 正文 年R本政府宣布辆布,位于F島的核電站瞬矩,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏锋玲。R本人自食惡果不足惜景用,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,025評(píng)論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望惭蹂。 院中可真熱鬧伞插,春花似錦、人聲如沸盾碗。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,511評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)置尔。三九已至杠步,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間榜轿,已是汗流浹背幽歼。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,611評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留谬盐,地道東北人甸私。 一個(gè)月前我還...
    沈念sama閱讀 49,081評(píng)論 3 377
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像飞傀,于是被迫代替她去往敵國(guó)和親皇型。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,675評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容