1. 程式人生 > >SpringBoot Profile使用詳解及配置原始碼解析

SpringBoot Profile使用詳解及配置原始碼解析

在實踐的過程中我們經常會遇到不同的環境需要不同配置檔案的情況,如果每換一個環境重新修改配置檔案或重新打包一次會比較麻煩,Spring Boot為此提供了Profile配置來解決此問題。

Profile的作用

Profile對應中文並沒有合適的翻譯,它的主要作用就是讓Spring Boot可以根據不同環境提供不同的配置功能支援。

我們經常遇到這樣的場景:有開發、測試、生產等環境,不同的環境又有不同的配置。如果每個環境在部署時都需要修改配置檔案將會非常麻煩,而通過Profile則可以輕鬆解決改問題。

Profile的基本使用

比如上述環境,我們可以在Spring Boot中建立4個檔案:

  • applcation.properties:公共配置
  • application-dev.properties:開發環境配置
  • application-test.properties:測試環境配置
  • application-prod.properties:生產環境配置

在applcation.properties中配置公共配置,然後通過如下配置啟用指定環境的配置:

spring.profiles.active = prod

其中“prod”對照檔名中application-prod.properties。Spring Boot在處理時會獲取配置檔案applcation.properties,然後通過指定的profile的值“prod”進行拼接,獲得application-prod.properties檔案的名稱和路徑。

舉例說明,比如在開發環境使用服務的埠為8080,而在生產環境中需要使用18080埠。那麼,在application-prod.properties中配置如下:

server.port=8080

而在application-prod.properties中配置為:

server.port=18080

在使用不同環境時,可以通過在applcation.properties中配置spring.profiles.active屬性值來進行指定(如上面示例),也可以通過啟動命令引數進行指定:

java -jar springboot.jar --spring.profiles.active=prod

這樣打包後的程式只需通過命令列引數就可以使用不同環境的配置檔案。

基於yml檔案型別

如果配置檔案是基於yml檔案型別,還可以將所有的配置放在同一個配置檔案中:

spring:
  profiles: 
    active: prod

server: 
  port: 18080
  
---
spring: 
  profiles: dev  
  
server: 
  port: 8080  
  
---
spring: 
  profiles: test  
  
server: 
  port: 8081    

上述配置以“---”進行分割,其中第一個位置的為預設的profile,比如上述配置啟動時,預設使用18080埠。如果想使用指定的埠同樣可以採用上述命令啟動時指定。

原始碼解析

下面帶大家簡單看一下在Spring Boot中針對Profile的基本處理流程(不會過度細化具體操作)。在Spring Boot啟動的過程中執行其run方法時,會執行如下一段程式碼:

public ConfigurableApplicationContext run(String... args) {
    // ...
    try {
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
        ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
        // ...
    } 
    // ...
}

其中prepareEnvironment方法的相關程式碼如下:

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
        ApplicationArguments applicationArguments) {
    // ...
    listeners.environmentPrepared(environment);
    // ...
}

在prepareEnvironment方法處理業務的過程中會呼叫SpringApplicationRunListeners的environmentPrepared方法釋出事件。該方法會遍歷註冊在spring.factories中SpringApplicationRunListener實現類,然後呼叫其environmentPrepared方法。

其中spring.factories中SpringApplicationRunListener實現類註冊為:

# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener

SpringApplicationRunListeners方法中的呼叫程式碼如下:

void environmentPrepared(ConfigurableEnvironment environment) {
    for (SpringApplicationRunListener listener : this.listeners) {
        listener.environmentPrepared(environment);
    }
}

其中listeners便是註冊的類的集合,這裡預設只有EventPublishingRunListener。它的environmentPrepared方法實現為:

@Override
public void environmentPrepared(ConfigurableEnvironment environment) {
    this.initialMulticaster
            .multicastEvent(new ApplicationEnvironmentPreparedEvent(this.application, this.args, environment));
}

其實就是釋出了一個監聽事件。該事件會被同樣註冊在spring.factories中的ConfigFileApplicationListener監聽到:

# Application Listeners
org.springframework.context.ApplicationListener=\
// ...
org.springframework.boot.context.config.ConfigFileApplicationListener
// ...

關於配置檔案的核心處理便在ConfigFileApplicationListener中完成。在該類中我們可以看到很多熟悉的常量:

public class ConfigFileApplicationListener implements EnvironmentPostProcessor, SmartApplicationListener, Ordered {

    private static final String DEFAULT_PROPERTIES = "defaultProperties";

    // Note the order is from least to most specific (last one wins)
    private static final String DEFAULT_SEARCH_LOCATIONS = "classpath:/,classpath:/config/,file:./,file:./config/";

    private static final String DEFAULT_NAMES = "application";
    // ...
}

比如Spring Boot預設尋找的配置檔案的名稱、預設掃描的類路徑等。

不僅如此,該類還實現了監聽器SmartApplicationListener介面和EnvironmentPostProcessor介面也就是擁有了監聽器和環境處理的功能。

其onApplicationEvent方法對接收到事件進行判斷,如果是ApplicationEnvironmentPreparedEvent事件則呼叫onApplicationEnvironmentPreparedEvent方法進行處理,程式碼如下:

@Override
public void onApplicationEvent(ApplicationEvent event) {
    if (event instanceof ApplicationEnvironmentPreparedEvent) {
        onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event);
    }
    if (event instanceof ApplicationPreparedEvent) {
        onApplicationPreparedEvent(event);
    }
}

onApplicationEnvironmentPreparedEvent會獲取到對應的EnvironmentPostProcessor並呼叫其postProcessEnvironment方法進行處理。而loadPostProcessors方法獲取的EnvironmentPostProcessor正是在spring.factories中配置的當前類。

private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
    List<EnvironmentPostProcessor> postProcessors = loadPostProcessors();
    postProcessors.add(this);
    AnnotationAwareOrderComparator.sort(postProcessors);
    for (EnvironmentPostProcessor postProcessor : postProcessors) {
        postProcessor.postProcessEnvironment(event.getEnvironment(), event.getSpringApplication());
    }
}

經過一系列的呼叫,最終呼叫到該類的如下方法:

protected void addPropertySources(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
    RandomValuePropertySource.addToEnvironment(environment);
    new Loader(environment, resourceLoader).load();
}

其中Loader類為ConfigFileApplicationListener內部類,提供了具體處理配置檔案優先順序、profile、載入解析等功能。

比如,在Loader類的load方法中便有如下一段程式碼:

for (PropertySourceLoader loader : this.propertySourceLoaders) {
    if (canLoadFileExtension(loader, location)) {
        load(loader, location, profile, filterFactory.getDocumentFilter(profile), consumer);
        return;
    }
}

該程式碼遍歷PropertySourceLoader列表,並進行對應配置檔案的解析,而這裡的列表中的PropertySourceLoader同樣配置在spring.factories中:

# PropertySource Loaders
org.springframework.boot.env.PropertySourceLoader=\
org.springframework.boot.env.PropertiesPropertySourceLoader,\
org.springframework.boot.env.YamlPropertySourceLoader

檢視這兩PropertySourceLoader的原始碼,會發現SpringBoot預設支援的配置檔案格式及解析方法。

public class PropertiesPropertySourceLoader implements PropertySourceLoader {

    private static final String XML_FILE_EXTENSION = ".xml";

    @Override
    public String[] getFileExtensions() {
        return new String[] { "properties", "xml" };
    }

    @Override
    public List<PropertySource<?>> load(String name, Resource resource) throws IOException {
        Map<String, ?> properties = loadProperties(resource);
        // ...
    }

    @SuppressWarnings({ "unchecked", "rawtypes" })
    private Map<String, ?> loadProperties(Resource resource) throws IOException {
        String filename = resource.getFilename();
        if (filename != null && filename.endsWith(XML_FILE_EXTENSION)) {
            return (Map) PropertiesLoaderUtils.loadProperties(resource);
        }
        return new OriginTrackedPropertiesLoader(resource).load();
    }
}

比如PropertiesPropertySourceLoader中支援了xml和properties兩種格式的配置檔案,並分別提供了PropertiesLoaderUtils和OriginTrackedPropertiesLoader兩個類進行相應的處理。

同樣的YamlPropertySourceLoader支援yml和yaml格式的配置檔案,並且採用OriginTrackedYamlLoader類進行解析。

public class YamlPropertySourceLoader implements PropertySourceLoader {

    @Override
    public String[] getFileExtensions() {
        return new String[] { "yml", "yaml" };
    }

    @Override
    public List<PropertySource<?>> load(String name, Resource resource) throws IOException {
        // ...
        List<Map<String, Object>> loaded = new OriginTrackedYamlLoader(resource).load();
        // ...
    }
}

當然,在ConfigFileApplicationListener類中還實現了上面提到的如何拼接預設配置檔案和profile的實現,相關程式碼如下:

private void loadForFileExtension(PropertySourceLoader loader, String prefix, String fileExtension,
        Profile profile, DocumentFilterFactory filterFactory, DocumentConsumer consumer) {
    // ...
    if (profile != null) {
        String profileSpecificFile = prefix + "-" + profile + fileExtension;
        load(loader, profileSpecificFile, profile, defaultFilter, consumer);
        load(loader, profileSpecificFile, profile, profileFilter, consumer);
        // Try profile specific sections in files we've already processed
        for (Profile processedProfile : this.processedProfiles) {
            if (processedProfile != null) {
                String previouslyLoaded = prefix + "-" + processedProfile + fileExtension;
                load(loader, previouslyLoaded, profile, profileFilter, consumer);
            }
        }
    }
    // ...
}

ConfigFileApplicationListener類中還實現了其他更多的功能,大家感興趣的話可以debug進行閱讀。

原文連結:《SpringBoot Profile使用詳解及配置原始碼解析》

Spring技術視訊

CSDN學院:《Spring Boot 視訊教程全家桶》


程式新視界:精彩和成長都不容錯過

相關推薦

SpringBoot Profile使用配置原始碼解析

在實踐的過程中我們經常會遇到不同的環境需要不同配置檔案的情況,如果每換一個環境重新修改配置檔案或重新打包一次會比較麻煩,Spring Boot為此提供了Profile配置來解決此問題。 Profile的作用 Profile對應中文並沒有合適的翻譯,它的主要作用就是讓Spring Boot可以根據不同環境提供不

Android 網路框架之Retrofit2使用原始碼解析原理

就目前來說Retrofit2使用的已相當的廣泛,那麼我們先來了解下兩個問題: 1 . 什麼是Retrofit? Retrofit是針對於Android/Java的、基於okHttp的、一種輕量級且安全的、並使用註解方式的網路請求框架。 2 . 我們為什麼要

nfs原理配置

1.0 而是 size 應用程序 roc alt 掛載文件系統 客戶 接受 簡介 工作原理 配置 簡介 NFS(Network File System)即網絡文件系統,它允許網絡中的計算機之間通過網絡共享資源。將NFS主機分享的目錄,掛載到本地客戶端當中,

Android應用ViewDragHelper部分原始碼淺析

1 背景 很久沒有更新部落格了,忙裡偷閒產出一篇。寫這片文章主要是去年專案中的一個需求,當時三下五除二的將其實現了,但是原始碼的閱讀卻一直扔在那遲遲沒有時間理會,現在揀起來看看吧,否則心裡一直不踏實。 關於啥是ViewDragHelper,這裡不再

Tomcat負載均衡原理配置

結構圖 使用Tomcat的童鞋們注意了。為了增加tomcat的效能和穩定性,我們一般採用balance和session同步機制。 下圖列出了我們常用也是最簡單的解決方案。 說明 1 balance 1.1 mod_proxy方式   mod_proxy是一種分工合作的的形式,通過主伺服器跳轉到各臺主

NAT技術配置例項

Outside local address:外部本地地址,不必是合法地址。當外部網路資料到達內部網路,外部網路中的主機IP地址與內部網路中的主機處在同一網段時,為防止內部主機誤認外部主機與自己在同一網段而廣播ARP請求,造成無法通訊,將外部主機的地址轉換成外部本地地址之後再與內部主機進行通訊。

Postgresql 非同步流複製 配置切換

####### 1, postgresql移步同步 and 主備切換 2, postgresql 同步 and 主備切換 3, archive 資料同步 參考: 參考: <<從小工到專家>> postgres hot_standby 主從配置部署: (hot_standy) 版本

【集合】HashMap原始碼解析

一、HashMap概述 二、HashMap的資料結構 三、HashMap原始碼分析 1.繼承 2、關鍵屬

selinux配置文件

nfs 禁用selinux 提示 ORC 我們 針對 可能 linux目錄 warn selinux詳解 selinux 的全稱是Security Enhance Linux,就是安全加強的Linux。在Selinux之前root賬號能夠任意的訪問所有文檔和服務 ; 如

SpringBoot之DispatcherServlet原始碼解析

在使用SpringBoot之後,我們表面上已經無法直接看到DispatcherServlet的使用了。本篇文章,帶大家從最初DispatcherServlet的使用開始到SpringBoot原始碼中DispatcherServlet的自動配置進行詳解。 DispatcherServlet簡介 Dispatch

【MapReduce原始碼解析(一)】——分片輸入、MapperMap端Shuffle過程

title: 【MapReduce詳解及原始碼解析(一)】——分片輸入、Mapper及Map端Shuffle過程 date: 2018-12-03 21:12:42 tags: Hadoop categories: 大資料 toc: true 點選檢視我的部落格:Josonlee’

Spring IOC容器啟動流程原始碼解析(一)——容器概念原始碼初探

目錄 1. 前言 1.1 IOC容器到底是什麼 IOC和AOP是Spring框架的核心功能,而IOC又是AOP實現的基礎,因而可以說IOC是整個Spring框架的基石。那麼什麼是IOC?IOC即控制反轉,通俗的說就是讓Spring框架來幫助我們完成物件的依賴管理和生命週期控制等等工作。從面向物件的角度來說,

Android應用Context原始碼解析

轉自 http://blog.csdn.net/yanbober/article/details/45967639  1  背景 今天突然想起之前在上家公司(做TV與BOX盒子)時有好幾個人問過我關於Android的Context到底是啥的問題,所以就馬上

OKHttp使用原始碼解析

前言 上一篇部落格瞭解了Retrofit的使用,它是對OKHttp的封裝,且Retrofit2的內部實現是OKHttp3,下面就瞭解一下OKHttp3的使用! 使用 ①首先匯入依賴,最新的版本是3.4.1,在gradle中: compile 'com

bash配置檔案/etc/profile,/etc/bashrc,~/.bash_profile,~/.bashrc修改PATH環境變數

 1.bash的配置檔案 1)、全域性配置與個人配置 全域性配置 /etc/profile, /rtc/profile.d/*.sh,/etc/bashrc 個人配置 ~/.bash_pr

nginx學習三 nginx配置解析程式碼實現

nginx配置項解析詳解及程式碼實現 0回顧  在上一節,用nginx簡單實現了一個hello world程式:當我們在瀏覽器中輸入lochost/hello ,瀏覽器就返回:hello world。為什麼會這樣呢,簡單一點說就是當我們請求訪問hello這個服務,ngi

DNS配置 bind實現正向解析和反向解析

cell 8.4 -s intern 資源記錄 ted borde linux系統 ans DNS是域名服務(Domain Name Service),負責把域名解析成IP地址(正向解析)或者把IP地址解析為域名(反向解析)。 DNS查詢過程: 假設我們要訪問www.a

redis配置文件實現主從同步切換

redis redis主從 redis配置文件詳解及實現主從同步切換redis復制Redis復制很簡單易用,它通過配置允許slave Redis Servers或者Master Servers的復制品。接下來有幾個關於redis復制的非常重要特性:一個Master可以有多個Slaves。Slaves能

Log4j配置文件實例

res 同時 生日 level iter printf %d ron inux    1 ) . 配置根 Logger ,其語法為:    log4j.rootLogger = [ level ] , appenderName, appenderName, …  其中, l

Haproxy 基礎動靜分離配置

haproxy 動靜分離 haproxy 介紹 1 工作在ISO 七層 根據http協議(或者工作在ISO四層 根據tcp協議) 提供web服務的負載均衡調度器負載均衡調度器分類 工作在四層: # lvs 工作在七層: # nginx (web,http reverse proxy,cache