這篇文章是《SpringBoot2.x入門》專輯的第4篇文章,使用的SpringBoot
版本爲2.3.1.RELEASE
,JDK
版本爲1.8
。java
主要介紹SpringBoot
配置文件一些經常使用屬性、配置文件的加載優先級以及一些和配置相關的注意事項。git
一個基於標準的引入了SpringBoot
組件的Maven
項目的結構通常以下:github
Root(項目根目錄) - src - main - java - resources # <-- 這個就是資源文件的存放目錄 - target pom.xml
資源文件存放在src/main/resouces
目錄下,而配置文件本質上也是資源文件,因此項目內的配置文件就存放於該目錄下。從SpringBoot
的屬性源加載器PropertySourceLoader
的實現來看,目前支持Properties
和Yaml
兩種配置文件類型。二者各有優點:Yaml
的配置屬性更靈活,而Properties
的配置不容易出錯(筆者前公司的技術規範中明確了SpringBoot
應用必須使用Properties
配置文件,由於運維或者開發同事曾由於生產配置使用了Yaml
格式的文件,編輯期間由於空格問題致使了嚴重的生產故障)。下文會使用Properties
配置文件做爲示例。web
SpringBoot
的配置文件使用了profile
(profile
自己就有剖面、配置文件的含義,下面會把profile
做爲一個專有名詞使用)的概念,能夠類比爲區分不一樣環境的標識符,一個SpringBoot
應用容許使用多個profile
,因此配置文件的格式必須爲application-${profile}.文件後綴
,例如:spring
src/main/resources - application.properties - application-dev.properties # <-- profile = dev,開發環境配置 - application-test.properties # <-- profile = test,測試環境配置
其中不帶profile
標識的application.properties
,能夠理解爲主配置文件,也就是SpringBoot
的配置文件其實有繼承關係,項目啓動時,主配置文件不管如何都會優先加載,而後被激活的profile
標識的配置文件纔會加載,能夠經過屬性spring.profiles.active
指定激活哪個profile
配置文件,如:shell
# 指定加載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
)。json
能夠經過自動裝配org.springframework.core.env.Environment
,經過Environment#getActiveProfiles()
獲取當前激活的profile
數組,例如:api
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
接口做爲展現,後面的文章會介紹該接口的使用方式。springboot
通常狀況下會引入spring-boot-starter-web
開發web
項目,有幾個基本配置筆者認爲是必須的。主配置文件application.properties
中應該標識應用名和默認選用的profile
,例如API
網關的主配置文件以下:
spring.application.name=api-gateway spring.profiles.active=dev
此外,主配置中間中應該配置一些不容易變更的屬性,例如Mybatis
的Mapper
掃描路徑、模板引擎Freemarker
的配置屬性等等。而profile
標識的配置文件中,應該配置一些跟隨環境變化的配置或者常常更變的屬性,例如MySQL
數據源的配置、Redis
的鏈接配置等等,以便經過spring.profiles.active
直接切換不一樣環境的中間件鏈接屬性或者第三方配置。在Hello World
類型的項目中,通常添加server.port
指定容器的啓動端口,如application-dev.properties
的內容以下:
server.port=8081
除了主配置文件會優先profile
標識的配置文件加載以外,SpringBoot
還支持經過文件系統加載配置文件,這些配置文件不必定在項目內(準確來講是項目編譯以後打出來的包內),還能夠存在於特定的磁盤路徑中。這一點能夠參考SpringBoot
官方文檔2.Externalized Configuration
一節:
默認的配置文件加載優先級順序是:
file:./config/
(項目部署包所在目錄的同級config
目錄下的application-[profile].[properties,yaml]
文件)file:./config/*/
(項目部署包所在目錄的同級config
目錄下的任意子目錄中的application-[profile].[properties,yaml]
文件)file:./
(項目部署包所在目錄的application-[profile].[properties,yaml]
文件)classpath:/config/
(項目部署包內類路徑下的config
目錄下的application-[profile].[properties,yaml]
文件)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/
,那麼配置文件加載優先級順序是:
file:./custom-config/
(項目部署包所在目錄的同級custom-config
目錄下的application-[profile].[properties,yaml]
文件)classpath:custom-config/
(項目部署包內類路徑下的custom-config
目錄下的application-[profile].[properties,yaml]
文件)file:./config/
(項目部署包所在目錄的同級config
目錄下的application-[profile].[properties,yaml]
文件)file:./config/*/
(項目部署包所在目錄的同級config
目錄下的任意子目錄中的application-[profile].[properties,yaml]
文件)file:./
(項目部署包所在目錄的application-[profile].[properties,yaml]
文件)classpath:/config/
(項目部署包內類路徑下的config
目錄下的application-[profile].[properties,yaml]
文件)classpath:/
(項目部署包內類路徑下的application-[profile].[properties,yaml]
文件)基於這個特性,在不對接配置中心的前提下,可讓運維夥伴在生產服務器上先配置好服務所需的生產環境的配置文件:
# 假設這個是生產服務器的文件路徑 /data/apps/api-gateway - api-gateway.jar - config - application-prod.properties
在編寫啓動腳本的時候只需指定profile
爲prod
便可,應用會讀取/data/apps/api-gateway/config/application-prod.properties
的屬性,這樣就能避免生產配置或者敏感屬性泄漏給開發人員。
這裏還有一個比較重要的問題就是:若是在多種路徑下的配置文件定義了同一個屬性,那麼屬性會依照一個優先級順序進行覆蓋。由於SpringBoot
除了配置文件,還支持命令行、JNDI
屬性、系統屬性等等,若是所有列舉會比較複雜,這裏按照目前分析過的內容列舉這個優先級順序:
application-profile.[properties,yaml]
文件。application-profile.[properties,yaml]
文件。application.[properties,yaml]
文件。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
如Eclipse
和IntelliJ 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),不按期推送筆者原創技術文章(毫不抄襲或者轉載):