springboot啓動流程(三)Environment簡介

全部文章

http://www.javashuo.com/article/p-uvudtich-bm.htmlhtml

 

簡介

上一篇文章中,咱們簡單瞭解了一下SpringApplication的run方法的代碼邏輯。其中的prepareEnvironment方法正如它的方法名錶示的意思同樣,爲當前應用準備一個Environment對象,也就是運行環境。在閱讀prepareEnvironment代碼以前,咱們先了解一下Environment。java

 

組成

首先,Environment是Spring3.1才提供的一個接口。它是對當前運行的應用程序的環境的抽象,下面咱們瞭解一下它的組成。react

Environment由兩部分組成web

1)profilesspring

profile中文直譯是"概述"、"簡介"、"輪廓"的意思,但在使用spring開發應用程序的時候,咱們對profile的認識更親切的是用在劃分多環境的時候。數據結構

一般,咱們會將profile劃分紅如:開發、測試、預生產、生產環境。每一個環境會有有些bean不一樣、配置不一樣等。每一個profile將有相應的bean和配置與之匹配,那麼當咱們切換profile的時候天然也就切換了相應的bean和配置文件,從而達到在不一樣環境中快速切換避免不斷修改的問題。app

這也就是spring的java doc裏面描述的"logical group"的意思。測試

2)propertiesthis

properties的概念想必咱們已經很是熟悉了,在java中properties表明着key-value的鍵值對象集合。Environment內部設計了key-value結構的對象來存儲相應的鍵值。spa

綜上所述,Environment中包含着用於切換環境的profile,還包含着存儲鍵值對的properties。

 

核心uml類圖

上面的內容中,咱們瞭解了Environment的組成部分包括profile和properties。spring在對Environment進行設計的時候也把這兩個部分進行了隔離。

如上圖所示,PropertyResolver包含了properties相關的操做,如:getProperty(String key),Environment繼承於PropertyResolver同時也就將properties的相關能力給組合了進來。

Environment的則包含了profile的相關操做,如:getActiveProfiles()。

若是查看PropertyResolver和Environment接口的方法,咱們就會發現這兩個接口都只是包含了如getter方法的獲取操做,並無setter樣子的操做。這或許也意味着spring但願在程序的開發運行過程當中,Environment儘可能是維持穩定的,而不是不斷地被修改、變化。

那麼在程序啓動過程當中勢必要對Environment進行配置,所以咱們會看到多個繼承自Environment和PropertyResolver接口地子接口,如:ConfigurableEnvironment和ConfigurablePropertyResolver。

再往下看,AbstractEnvironment顯然包含了Environment設計地大部分實現,而從StandardEnvironment再往下走了兩個分支,也就是針對reactive和Servlet的Environment實現。

到這裏,咱們基本瞭解了Environment主要的相關接口設計,設計路線也比較簡單。

 

profile和properties的數據結構

前面的兩個部分,咱們瞭解了Environment包含profile和properties。也知道了Environment相關接口也主要是根據profile和properties來設計的。可是咱們並不知道具體的實現裏面profile和properties的數據結構是怎麼樣的。

從uml類圖中,咱們清晰地看到Environment的具體實現是在AbstractEnvironment這個抽象類中。咱們能夠直接打開這個類

 

profile數據結構

AbstractEnvironment類中包含着profile的成員變量

private final Set<String> activeProfiles = new LinkedHashSet<>();

private final Set<String> defaultProfiles = new LinkedHashSet<>(getReservedDefaultProfiles());

profile的存儲結構看起來相對簡單,就是兩個set集合,每一個profile就是單純的一個String類型的字符串表示而已。

activeProfiles表示的是當前應用中"激活"的profile集合,好比我當profile=test的時候表示當前環境是測試環境。

而defaultProfiles則表示的是默認的profile集合,也就是說若是沒有任何指定的profile,那麼就會採用默認的。

 

properties數據結構

咱們再看看AbstractEnvironment中properties的數據結構

private final MutablePropertySources propertySources = new MutablePropertySources();

前面咱們一直提到,properties是一種key-value的鍵值對存儲的集合。那麼也就是說MutablePropertySources這個類實現了這個概念。

 

咱們先看看MutablePropertySources的繼承結構是怎麼樣的

看起來很簡單的設計路線,Iterable接口代表MutablePropertySources像集合同樣是能夠迭代的,咱們能夠大膽猜想其內部就是組成了一個集合。Iterable往下,就是PropertySources,這個接口表示的是PropertySource類的集合,也就是說被迭代的元素就是PropertySource。MutablePropertySources則直接繼承於PropertySources。

那麼,咱們基本能夠想獲得PropertySource這個類就是properties概念得設計,是咱們主要得關注對象。

如今讓咱們打開MutablePropertySources看看PropertySource的具體結構

public class MutablePropertySources implements PropertySources {

    private final List<PropertySource<?>> propertySourceList = new CopyOnWriteArrayList<>();

    // 省略
}

跟咱們想象得差很少,就是一個PropertySource類的集合做爲成員組合在MutablePropertySources中。

 

咱們繼續跟進PropertySource這個類,更多得了解一下

public abstract class PropertySource<T> {

    protected final String name;

    protected final T source;

    // 省略
}

看起來就是一個key-value的數據結構是嗎?這裏請注意!跟咱們想象的稍微有點不一樣,舉例說明

咱們建立了一個config.properties文件,內容如

username=test
password=a123456

那麼當config.properties這個文件被加載到內存中,並做爲一個PropertySource存在的時候,name=config而非name=username或者password。也就是說,加載config.properties這樣的資源,泛型T將會是一個Map集合,而Map集合包含着config.properties文件中全部的鍵值對。

 

另外,咱們注意到PropertySource是一個抽象類。spring將會針對資源的不一樣來源而使用不一樣的實現,例如上例中的config.properties加載到內存做爲Properties對象添加的,就是PropertySource的其中一個實現類PropertiesPropertySource。

還有諸如

1)來自命令行的配置:CommandLinePropertySource

2) 來自Servlet的配置:ServletConfigPropertySource、ServletContextPropertySource

下面是一張PropertySource的層級圖

 

prepareEnvironment建立Environment

上部分的內容包括了很多介紹的內容,下面咱們簡單看看SpringApplication的run方法中包含的prepareEnvironment方法,跟進方法

private ConfigurableEnvironment prepareEnvironment(
        SpringApplicationRunListeners listeners,
        ApplicationArguments applicationArguments
        ) {
    // 建立一個Environment對象
    ConfigurableEnvironment environment = getOrCreateEnvironment();
    // 配置Environment對象
    configureEnvironment(environment, applicationArguments.getSourceArgs());
    // 觸發監聽器(主要是觸發ConfigFileApplicationListener,這個監聽器將會加載如application.properties/yml這樣的配置文件)
    listeners.environmentPrepared(environment);
    bindToSpringApplication(environment);
    if (!this.isCustomEnvironment) {
        environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
                deduceEnvironmentClass());
    }
    ConfigurationPropertySources.attach(environment);
    return environment;
}

該方法核心內容包括三部分

1)建立一個Environment對象

2)配置Environment對象

3)觸發ConfigFileApplicationListener監聽器(加載application.properties/yml將再後續文章中說明)

 

getOrCreateEnvironment

咱們跟進getOrCreateEnvironment方法看看建立過程

private ConfigurableEnvironment getOrCreateEnvironment() {
    if (this.environment != null) {
        return this.environment;
    }
    switch (this.webApplicationType) {
    case SERVLET:
        return new StandardServletEnvironment();
    case REACTIVE:
        return new StandardReactiveWebEnvironment();
    default:
        return new StandardEnvironment();
    }
}

第一篇文章中,咱們提到SpringApplication在deduceFromClassPath方法中會推斷出WebApplicationType具體的枚舉實例,表明了當前應用的類型。

getOrCreateEnvironment方法中根據WebApplicationType類型選擇具體的Environment類型,也就是咱們提到過的Servlet類型、Reative類型或者非Web應用類型。

 

configureEnvironment

protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
    if (this.addConversionService) {
        ConversionService conversionService = ApplicationConversionService.getSharedInstance();
        environment.setConversionService((ConfigurableConversionService) conversionService);
    }
    // 添加初始的properties(注意:當前並未加載如application.properties/yml的properties)
    configurePropertySources(environment, args);
    // 添加初始的profile(注意:當前並未加載如application.properties/yml配置profile)
    configureProfiles(environment, args);
}

 

總結

本文,咱們大致地講解了Environment的接口設計、profile和properties的數據結構設計。再從prepareEnvironment方法中看到了Environment是根據webApplicationType匹配後建立的。到這裏,Environment相關的內容簡單介紹就結束了,咱們也初步地爲spring地Context建立了一個Environment的對象。

相關文章
相關標籤/搜索