一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - springboot中@Value的工作原理說明

springboot中@Value的工作原理說明

2021-09-28 09:49spring-hz Java教程

這篇文章主要介紹了springboot中@Value的工作原理,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教

我們知道springboot中的Bean組件的成員變量(屬性)如果加上了@Value注解,可以從有效的配置屬性資源中找到配置項進行綁定,那么這一切是怎么發生的呢?

下文將簡要分析一下@Value的工作原理。

springboot版本: springboot-2.0.6.RELEASE

概述

springboot啟動過程中,有兩個比較重要的過程,如下:

1 掃描,解析容器中的bean注冊到beanFactory上去,就像是信息登記一樣。

2 實例化、初始化這些掃描到的bean。

@Value的解析就是在第二個階段。BeanPostProcessor定義了bean初始化前后用戶可以對bean進行操作的接口方法,它的一個重要實現類AutowiredAnnotationBeanPostProcessor正如javadoc所說的那樣,為bean中的@Autowired和@Value注解的注入功能提供支持。

解析流程

調用鏈時序圖

@Value解析過程中的主要調用鏈,我用以下時序圖來表示:

springboot中@Value的工作原理說明

這里先簡單介紹一下圖上的幾個類的作用。

AbstractAutowireCapableBeanFactory: 提供了bean創建,屬性填充,自動裝配,初始胡。支持自動裝配構造函數,屬性按名稱和類型裝配。實現了AutowireCapableBeanFactory接口定義的createBean方法。

AutowiredAnnotationBeanPostProcessor: 裝配bean中使用注解標注的成員變量,setter方法, 任意的配置方法。比較典型的是@Autowired注解和@Value注解。

InjectionMetadata: 類的注入元數據,可能是類的方法或屬性等,在AutowiredAnnotationBeanPostProcessor類中被使用。

AutowiredFieldElement: 是AutowiredAnnotationBeanPostProcessor的一個私有內部類,繼承InjectionMetadata.InjectedElement,描述注解的字段。

StringValueResolver: 一個定義了處置字符串值的接口,只有一個接口方法resolveStringValue,可以用來解決占位符字符串。本文中的主要實現類在PropertySourcesPlaceholderConfigurer#processProperties方法中通過lamda表達式定義的。供ConfigurableBeanFactory類使用。

PropertySourcesPropertyResolver: 屬性資源處理器,主要功能是獲取PropertySources屬性資源中的配置鍵值對。

PropertyPlaceholderHelper: 一個工具類,用來處理帶有占位符的字符串。形如${name}的字符串在該工具類的幫助下,可以被用戶提供的值所替代。替代途經可能通過Properties實例或者PlaceholderResolver(內部定義的接口)。

PropertyPlaceholderConfigurerResolver: 上一行所說的PlaceholderResolver接口的一個實現類,是PropertyPlaceholderConfigurer類的一個私有內部類。實現方法resolvePlaceholder中調用了外部類的resolvePlaceholder方法。

調用鏈說明

這里主要介紹一下調用鏈中的比較重要的方法。

AbstractAutowireCapableBeanFactory#populateBean方法用于填充bean屬性,執行完后可獲取屬性裝配后的bean。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
protected void populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper bw) {      
...
if (hasInstAwareBpps) {
    // 遍歷所有InstantiationAwareBeanPostProcessor實例設置屬性字段值。
    for (BeanPostProcessor bp : getBeanPostProcessors()) {
        // AutowiredAnnotationBeanPostProcessor會進入此分支
        if (bp instanceof InstantiationAwareBeanPostProcessor) {
            InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
            pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
        //上行代碼執行后,bw.getWrappedInstance()就得到了@Value注解裝配屬性后的bean了
            if (pvs == null) {
                return;
            }
        }
    }
}
...
}

InjectionMetadata#inject逐個裝配bean的配置屬性。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
    Collection<InjectedElement> checkedElements = this.checkedElements;
    Collection<InjectedElement> elementsToIterate =
            (checkedElements != null ? checkedElements : this.injectedElements);
    if (!elementsToIterate.isEmpty()) {
        // 依次注入屬性
        for (InjectedElement element : elementsToIterate) {
            if (logger.isDebugEnabled()) {
                logger.debug("Processing injected element of bean '" + beanName + "': " + element);
            }
            element.inject(target, beanName, pvs);
        }
    }
}

PropertyPlaceholderHelper#parseStringValue解析屬性值

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
/**
 *  一個參數示例 value = "${company.ceo}"
 *
 */
protected String parseStringValue(
        String value, PlaceholderResolver placeholderResolver, Set<String> visitedPlaceholders) {
    StringBuilder result = new StringBuilder(value);
    // this.placeholderPrefix = "${"
    int startIndex = value.indexOf(this.placeholderPrefix);
    while (startIndex != -1) {
        // 占位符的結束位置,以value = "${company.ceo}"為例,endIndex=13
        int endIndex = findPlaceholderEndIndex(result, startIndex);
        if (endIndex != -1) {
            // 獲取{}里的真正屬性名稱,此例為"company.ceo"
            String placeholder = result.substring(startIndex + this.placeholderPrefix.length(), endIndex);
            String originalPlaceholder = placeholder;
            if (!visitedPlaceholders.add(originalPlaceholder)) {
                throw new IllegalArgumentException(
                        "Circular placeholder reference '" + originalPlaceholder + "' in property definitions");
            }
            // Recursive invocation, parsing placeholders contained in the placeholder key.
            // 遞歸調用本方法,因為屬性鍵中可能仍然有占位符
            placeholder = parseStringValue(placeholder, placeholderResolver, visitedPlaceholders);
            // Now obtain the value for the fully resolved key...
            // 獲取屬性鍵placeholder對應的屬性值
            String propVal = placeholderResolver.resolvePlaceholder(placeholder);
            // 此處邏輯是當company.ceo=${bi:li}時,company.ceo最終被li所替代的原因
            // 所以配置文件中,最好不要出現類似${}的東西,因為它本身就會被spring框架所解析
            if (propVal == null && this.valueSeparator != null) {
                int separatorIndex = placeholder.indexOf(this.valueSeparator);
                if (separatorIndex != -1) {
                    String actualPlaceholder = placeholder.substring(0, separatorIndex);
                    String defaultValue = placeholder.substring(separatorIndex + this.valueSeparator.length());
                    propVal = placeholderResolver.resolvePlaceholder(actualPlaceholder);
                    if (propVal == null) {
                        propVal = defaultValue;
                    }
                }
            }
            if (propVal != null) {
                // Recursive invocation, parsing placeholders contained in the
                // previously resolved placeholder value.
                propVal = parseStringValue(propVal, placeholderResolver, visitedPlaceholders);
                // 將${company.ceo}替換為li
                result.replace(startIndex, endIndex + this.placeholderSuffix.length(), propVal);
                if (logger.isTraceEnabled()) {
                    logger.trace("Resolved placeholder '" + placeholder + "'");
                }
                startIndex = result.indexOf(this.placeholderPrefix, startIndex + propVal.length());
            }
            else if (this.ignoreUnresolvablePlaceholders) {
                // Proceed with unprocessed value.
                startIndex = result.indexOf(this.placeholderPrefix, endIndex + this.placeholderSuffix.length());
            }
            else {
                throw new IllegalArgumentException("Could not resolve placeholder '" +
                        placeholder + "'" + " in value \"" + value + "\"");
            }
            visitedPlaceholders.remove(originalPlaceholder);
        }
        else {
            startIndex = -1;
        }
    }
    return result.toString();
}

總結

@Value注解標注的bean屬性裝配是依靠AutowiredAnnotationBeanPostProcessor在bean的實例化、初始化階段完成的。以上為個人經驗,希望能給大家一個參考,也希望大家多多支持服務器之家。

原文鏈接:https://blog.csdn.net/gs_albb/article/details/85401720

延伸 · 閱讀

精彩推薦
  • Java教程升級IDEA后Lombok不能使用的解決方法

    升級IDEA后Lombok不能使用的解決方法

    最近看到提示IDEA提示升級,尋思已經有好久沒有升過級了。升級完畢重啟之后,突然發現好多錯誤,本文就來介紹一下如何解決,感興趣的可以了解一下...

    程序猿DD9332021-10-08
  • Java教程小米推送Java代碼

    小米推送Java代碼

    今天小編就為大家分享一篇關于小米推送Java代碼,小編覺得內容挺不錯的,現在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧...

    富貴穩中求8032021-07-12
  • Java教程Java實現搶紅包功能

    Java實現搶紅包功能

    這篇文章主要為大家詳細介紹了Java實現搶紅包功能,采用多線程模擬多人同時搶紅包,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙...

    littleschemer13532021-05-16
  • Java教程20個非常實用的Java程序代碼片段

    20個非常實用的Java程序代碼片段

    這篇文章主要為大家分享了20個非常實用的Java程序片段,對java開發項目有所幫助,感興趣的小伙伴們可以參考一下 ...

    lijiao5352020-04-06
  • Java教程xml與Java對象的轉換詳解

    xml與Java對象的轉換詳解

    這篇文章主要介紹了xml與Java對象的轉換詳解的相關資料,需要的朋友可以參考下...

    Java教程網2942020-09-17
  • Java教程Java8中Stream使用的一個注意事項

    Java8中Stream使用的一個注意事項

    最近在工作中發現了對于集合操作轉換的神器,java8新特性 stream,但在使用中遇到了一個非常重要的注意點,所以這篇文章主要給大家介紹了關于Java8中S...

    阿杜7472021-02-04
  • Java教程Java BufferWriter寫文件寫不進去或缺失數據的解決

    Java BufferWriter寫文件寫不進去或缺失數據的解決

    這篇文章主要介紹了Java BufferWriter寫文件寫不進去或缺失數據的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望...

    spcoder14552021-10-18
  • Java教程Java使用SAX解析xml的示例

    Java使用SAX解析xml的示例

    這篇文章主要介紹了Java使用SAX解析xml的示例,幫助大家更好的理解和學習使用Java,感興趣的朋友可以了解下...

    大行者10067412021-08-30
主站蜘蛛池模板: 国产精品色拉拉免费看 | 精品国产免费久久久久久 | 九草视频在线 | 国产肥女bbwbbw | 99在线观看国产 | 亚洲情欲网| 无限资源在线观看完整版免费下载 | 嫩草影院精品视频在线观看 | 亚洲天堂精品在线观看 | 欧美大片一级片 | youzljzljzljzlj96| 高清视频免费 | 欧美高清3dfreexxxx性 | 亚洲欧美精品久久 | 亚洲 欧美 国产 在线 日韩 | 国产精品色拉拉免费看 | 扒开老女人 | 四虎影视紧急入口地址大全 | xxxxx性中国hd | 国产极品美女在线 | 色综色天天综合网 | 午夜看片a福利在线观看 | 第一福利在线观看永久视频 | 国产成人免费a在线资源 | 欧美日韩久久中文字幕 | 欧美亚洲一区二区三区 | 香蕉免费一区二区三区 | 国色天香高清版 | 亚洲AV国产福利精品在现观看 | 蜜桃麻豆 | 明星裸乳照无奶罩 | 亚洲天堂中文 | 国产精品制服丝袜白丝www | 四虎影视入口 | 韩日一区二区三区 | 成人网视频免费播放 | 肥奶丰熟肥妇 | 洗濯屋动漫在线观看 | 久久精品国产免费播放 | avtt天堂网手机版亚洲 | 亚洲系列国产精品制服丝袜第 |