SpringBoot源碼解析 -- @Value,@Autowired實(shí)現(xiàn)原理

SpringBoot深入理解 -- @AliasFor注解的作用
SpringBoot源碼解析 -- SpringBoot啟動(dòng)過程
SpringBoot源碼解析 -- AutoConfigure的實(shí)現(xiàn)原理
SpringBoot源碼解析 -- @ComponentScan的實(shí)現(xiàn)原理
SpringBoot源碼解析 -- @Value,@Autowired實(shí)現(xiàn)原理
SpringBoot源碼解析 -- Tomcat棍潘,SpringMVC啟動(dòng)
SpringBoot源碼解析 -- Logging渣锦,Environment啟動(dòng)

前面解析@ComponentScan實(shí)現(xiàn)原理的文章說了尝盼,Spring如何掃描@Component注解的Bean,但掃描的Bean如何注入屬性呢氮发?
我們都知道該功能主要使用@Value,@Autowired注解完成,這篇文章主要解析Spring中@Value,@Autowired實(shí)現(xiàn)原理酥艳。
源碼分析基于spring boot 2.1

前面解析Spring注入屬性過程的文章中,說過AbstractAutowireCapableBeanFactory#populateBean方法負(fù)責(zé)注入bean的屬性爬骤,其實(shí)@Value充石,@Autowired等注解也是在該方法處理。

protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
    ...
    PropertyDescriptor[] filteredPds = null;
    if (hasInstAwareBpps) {
        if (pvs == null) {
            pvs = mbd.getPropertyValues();
        }
        for (BeanPostProcessor bp : getBeanPostProcessors()) {
            if (bp instanceof InstantiationAwareBeanPostProcessor) {
                InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
                // #1
                PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
                if (pvsToUse == null) {
                    if (filteredPds == null) {
                        filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
                    }
                    pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
                    if (pvsToUse == null) {
                        return;
                    }
                }
                pvs = pvsToUse;
            }
        }
    }
    ...
}

#1 調(diào)用InstantiationAwareBeanPostProcessor#postProcessProperties擴(kuò)展方法霞玄,這里處理@Value骤铃,@Autowired等注解。

在解析SpringBoot啟動(dòng)過程的文章中坷剧,說過了AutowiredAnnotationBeanPostProcessor負(fù)責(zé)處理@Value惰爬,@Autowired等注解。
(AutowiredAnnotationBeanPostProcessor是InstantiationAwareBeanPostProcessor的實(shí)現(xiàn)類)
AutowiredAnnotationBeanPostProcessor#postProcessProperties

public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
    // #1
    InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);    
    try {
        // #2
        metadata.inject(bean, beanName, pvs);   
    }
    ...
    return pvs;
}

#1 獲取Class中關(guān)于屬性注入相關(guān)注解的元數(shù)據(jù)
#2 完成屬性注入操作

AutowiredAnnotationBeanPostProcessor#findAutowiringMetadata -> buildAutowiringMetadata

    private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {
        LinkedList<InjectionMetadata.InjectedElement> elements = new LinkedList<InjectionMetadata.InjectedElement>();
        Class<?> targetClass = clazz;

        do {
            final LinkedList<InjectionMetadata.InjectedElement> currElements =
                    new LinkedList<InjectionMetadata.InjectedElement>();

            ReflectionUtils.doWithLocalFields(targetClass, new ReflectionUtils.FieldCallback() {
                @Override
                public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
                    // #1
                    AnnotationAttributes ann = findAutowiredAnnotation(field);
                    if (ann != null) {
                        if (Modifier.isStatic(field.getModifiers())) {
                            if (logger.isWarnEnabled()) {
                                logger.warn("Autowired annotation is not supported on static fields: " + field);
                            }
                            return;
                        }
                        boolean required = determineRequiredStatus(ann);
                        currElements.add(new AutowiredFieldElement(field, required));
                    }
                }
            });

            ... // #2

            elements.addAll(0, currElements);
            targetClass = targetClass.getSuperclass();
        }
        while (targetClass != null && targetClass != Object.class);

        return new InjectionMetadata(clazz, elements);
    }

#1 查找field是否有@Autowired惫企,@Value等注解
#2 查找method是否有@Autowired撕瞧,@Value等注解

InjectionMetadata#inject方法遍歷所有的InjectedElement,調(diào)用AutowiredMethodElement雅任,AutowiredFieldElement的inject方法风范,這里只關(guān)注AutowiredFieldElement#inject
InjectionMetadata#inject -> AutowiredFieldElement#inject

protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable {
    Field field = (Field) this.member;
    Object value;
    if (this.cached) {
        value = resolvedCachedArgument(beanName, this.cachedFieldValue);
    }
    else {
        DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
        desc.setContainingClass(bean.getClass());
        Set<String> autowiredBeanNames = new LinkedHashSet<String>(1);
        TypeConverter typeConverter = beanFactory.getTypeConverter();
        try {
            // #1
            value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);   
        }
        catch (BeansException ex) {
            throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex);
        }
        ...
    }
    if (value != null) {
        // #2
        ReflectionUtils.makeAccessible(field);  
        field.set(bean, value);
    }
}

#1 根據(jù)注解元數(shù)據(jù),解析屬性值
#2 將解析的結(jié)果值注入到bean

DefaultListableBeanFactory#resolveDependency -> DefaultListableBeanFactory#doResolveDependency

public Object doResolveDependency(DependencyDescriptor descriptor, String beanName,
        Set<String> autowiredBeanNames, TypeConverter typeConverter) throws BeansException {

    InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor);
    try {
        Object shortcut = descriptor.resolveShortcut(this);
        if (shortcut != null) {
            return shortcut;
        }

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

        // #4
        Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);   
        if (multipleBeans != null) {
            return multipleBeans;
        }

        // #5
        Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor); 
        if (matchingBeans.isEmpty()) {
            if (isRequired(descriptor)) {
                raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
            }
            return null;
        }

        String autowiredBeanName;
        Object instanceCandidate;

        // #6
        if (matchingBeans.size() > 1) {
            autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);  
            if (autowiredBeanName == null) {
                if (isRequired(descriptor) || !indicatesMultipleBeans(type)) {
                    return descriptor.resolveNotUnique(type, matchingBeans);
                }
                else {
                    return null;
                }
            }
            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);
        }
        return (instanceCandidate instanceof Class ?
                descriptor.resolveCandidate(autowiredBeanName, type, this) : instanceCandidate);
    }
    finally {
        ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint);
    }
}

#1 如果存在@Value注解沪么,獲取注解的value值
#2 解析@Value注解的值硼婿,如果這里使用占位符引用了配置文件,默認(rèn)使用PropertyPlaceholderConfigurer#PlaceholderResolvingStringValueResolver解析配置文件中的值禽车。
PropertyPlaceholderConfigurer是一個(gè)常用的BeanFactoryPostProcessor寇漫,它可以在Spring啟動(dòng)時(shí)刊殉,將Bean屬性配置的占位符解析為配置屬性的值
#3 默認(rèn)使用SimpleTypeConverter將配置值轉(zhuǎn)化為屬性要求的類型(例如屬性為int類型,則需要將String轉(zhuǎn)換為int類型)
這里最終也會(huì)復(fù)用TypeConverterDelegate#convertIfNecessary方法州胳。
#4 解析數(shù)組记焊、list、map等類型的依賴栓撞,使用對(duì)應(yīng)的TypeConverter處理
#5 處理@Autowire遍膜,找到候選的bean
#6 存在多個(gè)候選的bean,spring要按優(yōu)先級(jí)決定用哪個(gè)瓤湘,較復(fù)雜瓢颅,不深入。

DefaultListableBeanFactory#findAutowireCandidates

protected Map<String, Object> findAutowireCandidates(
        String beanName, Class<?> requiredType, DependencyDescriptor descriptor) {

    // #1
    String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
            this, requiredType, true, descriptor.isEager());    
    Map<String, Object> result = new LinkedHashMap<String, Object>(candidateNames.length);
    // #2
    for (Class<?> autowiringType : this.resolvableDependencies.keySet()) {  
        if (autowiringType.isAssignableFrom(requiredType)) {
            Object autowiringValue = this.resolvableDependencies.get(autowiringType);
            autowiringValue = AutowireUtils.resolveAutowiringValue(autowiringValue, requiredType);
            if (requiredType.isInstance(autowiringValue)) {
                result.put(ObjectUtils.identityToString(autowiringValue), autowiringValue);
                break;
            }
        }
    }
    //#3
    for (String candidate : candidateNames) {
        if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, descriptor)) {  
            addCandidateEntry(result, candidate, descriptor, requiredType);
        }
    }
    ...
    return result;
}

#1 按類型查找spring上下文中所有的bean
#2 從當(dāng)前已存在的依賴關(guān)系中查找
#3 遍歷#1步驟中找到的bean弛说,如果滿足Autowire要求挽懦,添加到結(jié)果
這里檢查候選類的類型是否匹配,是否滿足@Qualifier的要求木人,不繼續(xù)深入信柿,有興趣的同學(xué)自行閱讀源碼。

如果您覺得本文不錯(cuò)醒第,歡迎關(guān)注我的微信公眾號(hào)渔嚷,您的關(guān)注是我堅(jiān)持的動(dòng)力!


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末稠曼,一起剝皮案震驚了整個(gè)濱河市圃伶,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌蒲列,老刑警劉巖窒朋,帶你破解...
    沈念sama閱讀 216,744評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異蝗岖,居然都是意外死亡侥猩,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,505評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門抵赢,熙熙樓的掌柜王于貴愁眉苦臉地迎上來欺劳,“玉大人,你說我怎么就攤上這事铅鲤』幔” “怎么了?”我有些...
    開封第一講書人閱讀 163,105評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵邢享,是天一觀的道長(zhǎng)鹏往。 經(jīng)常有香客問我,道長(zhǎng)骇塘,這世上最難降的妖魔是什么伊履? 我笑而不...
    開封第一講書人閱讀 58,242評(píng)論 1 292
  • 正文 為了忘掉前任韩容,我火速辦了婚禮,結(jié)果婚禮上唐瀑,老公的妹妹穿的比我還像新娘群凶。我一直安慰自己,他們只是感情好哄辣,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,269評(píng)論 6 389
  • 文/花漫 我一把揭開白布请梢。 她就那樣靜靜地躺著,像睡著了一般力穗。 火紅的嫁衣襯著肌膚如雪溢陪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,215評(píng)論 1 299
  • 那天睛廊,我揣著相機(jī)與錄音,去河邊找鬼杉编。 笑死超全,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的邓馒。 我是一名探鬼主播嘶朱,決...
    沈念sama閱讀 40,096評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼光酣!你這毒婦竟也來了疏遏?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,939評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤救军,失蹤者是張志新(化名)和其女友劉穎财异,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體唱遭,經(jīng)...
    沈念sama閱讀 45,354評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡戳寸,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,573評(píng)論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了拷泽。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片疫鹊。...
    茶點(diǎn)故事閱讀 39,745評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖司致,靈堂內(nèi)的尸體忽然破棺而出拆吆,到底是詐尸還是另有隱情,我是刑警寧澤脂矫,帶...
    沈念sama閱讀 35,448評(píng)論 5 344
  • 正文 年R本政府宣布枣耀,位于F島的核電站,受9級(jí)特大地震影響庭再,放射性物質(zhì)發(fā)生泄漏奕枢。R本人自食惡果不足惜娄昆,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,048評(píng)論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望缝彬。 院中可真熱鬧萌焰,春花似錦、人聲如沸谷浅。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,683評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽一疯。三九已至撼玄,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間墩邀,已是汗流浹背掌猛。 一陣腳步聲響...
    開封第一講書人閱讀 32,838評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留眉睹,地道東北人荔茬。 一個(gè)月前我還...
    沈念sama閱讀 47,776評(píng)論 2 369
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像竹海,于是被迫代替她去往敵國(guó)和親慕蔚。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,652評(píng)論 2 354