SpringBoot2.x入門教程:理解配置文件


前提

這篇文章是《SpringBoot2.x入門》專輯的第4篇文章,使用的SpringBoot版本為2.3.1.RELEASEJDK版本為1.8

主要介紹SpringBoot配置文件一些常用屬性、配置文件的加載優先級以及一些和配置相關的注意事項。

關於SpringBoot的配置文件

一個基於標准的引入了SpringBoot組件的Maven項目的結構一般如下:

Root(項目根目錄)
  - src
   - main
    - java
    - resources  # <-- 這個就是資源文件的存放目錄
  - target   
  pom.xml

資源文件存放在src/main/resouces目錄下,而配置文件本質上也是資源文件,所以項目內的配置文件就存放於該目錄下。從SpringBoot的屬性源加載器PropertySourceLoader的實現來看,目前支持PropertiesYaml兩種配置文件類型。兩者各有優勢:Yaml的配置屬性更靈活,而Properties的配置不容易出錯(筆者前公司的技術規范中明確了SpringBoot應用必須使用Properties配置文件,因為運維或者開發同事曾因為生產配置使用了Yaml格式的文件,編輯期間因為空格問題導致了嚴重的生產故障)。下文會使用Properties配置文件作為示例。

SpringBoot的配置文件使用了profileprofile本身就有剖面、配置文件的含義,下面會把profile作為一個專有名詞使用)的概念,可以類比為區分不同環境的標識符,一個SpringBoot應用允許使用多個profile,所以配置文件的格式必須為application-${profile}.文件后綴,例如:

src/main/resources
   - application.properties
   - application-dev.properties  # <-- profile = dev,開發環境配置
   - application-test.properties # <-- profile = test,測試環境配置

其中不帶profile標識的application.properties,可以理解為主配置文件,也就是SpringBoot的配置文件其實有繼承關系,項目啟動時,主配置文件無論如何都會優先加載,然后被激活的profile標識的配置文件才會加載,可以通過屬性spring.profiles.active指定激活哪一個profile配置文件,如:

# 指定加載application-dev.properties
spring.profiles.active=dev

# 或者同時加載application-dev.properties和application-test.properties
spring.profiles.active=dev,test

spring.profiles.active一般可以在主配置文件application.properties中指定,獲取通過啟動命令參數指定(java -jar -Dspring.profiles.active=prod app.jar或者java -jar app.jar --spring.profiles.active=prod)。

可以通過自動裝配org.springframework.core.env.Environment,通過Environment#getActiveProfiles()獲取當前激活的profile數組,例如:

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.core.env.Environment;
import java.util.Arrays;

@Slf4j
@SpringBootApplication
public class Ch2Application implements CommandLineRunner {

    @Autowired
    private Environment environment;

    public static void main(String[] args) {
        SpringApplication.run(Ch2Application.class, args);
    }

    @Override
    public void run(String... args) throws Exception {
        log.info("Active profiles:{}", Arrays.toString(environment.getActiveProfiles()));
    }
}

運行結果如下:

這里用到了CommandLineRunner接口作為展示,后面的文章會介紹該接口的使用方式。

常用的基本配置屬性

一般情況下會引入spring-boot-starter-web開發web項目,有幾個基本配置筆者認為是必須的。主配置文件application.properties中應該標識應用名和默認選用的profile,例如API網關的主配置文件如下:

spring.application.name=api-gateway
spring.profiles.active=dev

此外,主配置中間中應該配置一些不容易變動的屬性,例如MybatisMapper掃描路徑、模板引擎Freemarker的配置屬性等等。而profile標識的配置文件中,應該配置一些跟隨環境變化的配置或者經常更變的屬性,例如MySQL數據源的配置、Redis的連接配置等等,以便通過spring.profiles.active直接切換不同環境的中間件連接屬性或者第三方配置。在Hello World類型的項目中,一般添加server.port指定容器的啟動端口,如application-dev.properties的內容如下:

server.port=8081

配置文件加載優先級與屬性覆蓋

除了主配置文件會優先profile標識的配置文件加載之外,SpringBoot還支持通過文件系統加載配置文件,這些配置文件不一定在項目內(准確來說是項目編譯之后打出來的包內),還可以存在於特定的磁盤路徑中。這一點可以參考SpringBoot官方文檔2.Externalized Configuration一節:

默認的配置文件加載優先級順序是:

  1. file:./config/(項目部署包所在目錄的同級config目錄下的application-[profile].[properties,yaml]文件)
  2. file:./config/*/(項目部署包所在目錄的同級config目錄下的任意子目錄中的application-[profile].[properties,yaml]文件)
  3. file:./(項目部署包所在目錄的application-[profile].[properties,yaml]文件)
  4. classpath:/config/(項目部署包內類路徑下的config目錄下的application-[profile].[properties,yaml]文件)
  5. classpath:/(項目部署包內類路徑下的application-[profile].[properties,yaml]文件)

眼尖的伙伴可能會發現,在項目中的resources目錄下添加的配置文件的加載優先級是最低的(打包后相當於第5條)。可以通過spring.config.location屬性覆蓋上面的順序,如spring.config.location=classpath:/,classpath:/config/,一般不建議改變默認的配置順序,除非有特殊的使用場景。

另外,還可以通過spring.config.additional-location屬性指定額外附加的搜索配置文件的路徑,並且優先級比默認的配置順序要高,假如只配置了spring.config.additional-location=classpath:/custom-config/,file:./custom-config/,那么配置文件加載優先級順序是:

  1. file:./custom-config/(項目部署包所在目錄的同級custom-config目錄下的application-[profile].[properties,yaml]文件)
  2. classpath:custom-config/(項目部署包內類路徑下的custom-config目錄下的application-[profile].[properties,yaml]文件)
  3. file:./config/(項目部署包所在目錄的同級config目錄下的application-[profile].[properties,yaml]文件)
  4. file:./config/*/(項目部署包所在目錄的同級config目錄下的任意子目錄中的application-[profile].[properties,yaml]文件)
  5. file:./(項目部署包所在目錄的application-[profile].[properties,yaml]文件)
  6. classpath:/config/(項目部署包內類路徑下的config目錄下的application-[profile].[properties,yaml]文件)
  7. classpath:/(項目部署包內類路徑下的application-[profile].[properties,yaml]文件)

基於這個特性,在不對接配置中心的前提下,可以讓運維伙伴在生產服務器上先配置好服務所需的生產環境的配置文件:

# 假設這個是生產服務器的文件路徑
/data/apps/api-gateway
     - api-gateway.jar
     - config
       - application-prod.properties

在編寫啟動腳本的時候只需指定profileprod即可,應用會讀取/data/apps/api-gateway/config/application-prod.properties的屬性,這樣就能避免生產配置或者敏感屬性泄漏給開發人員。

這里還有一個比較重要的問題就是:如果在多種路徑下的配置文件定義了同一個屬性,那么屬性會依照一個優先級順序進行覆蓋。因為SpringBoot除了配置文件,還支持命令行、JNDI屬性、系統屬性等等,如果全部列舉會比較復雜,這里按照目前分析過的內容列舉這個優先級順序:

  1. 命令行中的屬性參數。
  2. 項目部署包之外的application-profile.[properties,yaml]文件。
  3. 項目部署包內的application-profile.[properties,yaml]文件。
  4. 項目部署包之外的application.[properties,yaml]文件。
  5. 項目部署包內的application.[properties,yaml]文件。

舉個例子,假如啟動參數中添加--app.author=throwable,配置文件application.properties中添加屬性app.author=throwable-x,而配置文件application-dev.properties中添加屬性app.author=throwable-y,那么使用profile=dev啟動應用的時候,優先獲取到的是屬性app.author=throwable

如果看過SpringBoot屬性加載的源碼可知,其實屬性優先級的思路在設計屬性加載模塊的時候正好相反,所有的配置文件都會進行解析,構成一個復合的PropertySource,后解析的參數總是在頂層,然后獲取屬性的時候,總是先從頂層獲取。

自定義配置屬性與IDE親和性

有時候需要配置自定義屬性,會出現在IDE中會無法識別而"標黃"的場景。這個時候可以應用IDE親和性。在主流的IDEEclipseIntelliJ IDEA中,只需要引入SpringBoot的屬性元數據描述文件(spring-configuration-metadata.json或者additional-spring-configuration-metadata.json),即可讓IDE識別,提供目錄引導跳轉的功能,不再"標黃"。具體的做法是在項目的resources/META-INF目錄中引入屬性元數據描述文件,然后編寫屬性描述即可:

// resources/META-INF/spring-configuration-metadata.json
{
  "properties": [
    {
      "name": "app.author",
      "type": "java.lang.String",
      "description": "The author of app."
    }
  ]
}

spring-configuration-metadata.json文件的格式可以參考SpringBoot多個starter中已經存在的文件,完成這一點,代碼潔癖患者或者強迫症患者會感覺良好

小結

這篇文章簡單總結了配置文件加載的優先級順序和配置屬性的覆蓋優先級順序,這兩點需要完全掌握,可以自行通過一些例子改變一下配置文件進行熟悉。配置屬性覆蓋的問題很容易導致生產故障,如果掌握了本節的內容,對於SpringBoot配置屬性方面的問題應該可以快速定位和解決。代碼倉庫:

(本文完 c-2-d e-a-20200705)

技術公眾號《Throwable文摘》(id:throwable-doge),不定期推送筆者原創技術文章(絕不抄襲或者轉載):


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM