Spring Boot 的設定可以從很多地方讀取,從程式內 System.setProperty()、命令列 --server.port=80、環境變數 SERVER_PORT、到 application.properties 跟 application-{profile}.properties 都算。當同一個 key 在多個來源都有定義,到底會用哪個?這篇文章整理常見的優先順序,以及容易踩坑的細節。如果想了解的是「怎麼切換 profile」而不是覆蓋順序,可以參考另一篇「指定 Spring Boot 執行環境的多種方式」。
本文以修改內建伺服器的 server.port 為例示範。
優先級總覽
下表整理 Spring Boot 常見 Property Source 由高到低的優先順序(高優先級會覆蓋低優先級)。實際 Spring Boot 文件列出的項目超過 20 個,這邊只挑工作中會用到的。
| 優先級 | 來源 | 常見用法 |
|---|---|---|
| 1(最高) | 命令列參數 | --server.port=80 |
| 2 | SPRING_APPLICATION_JSON | 環境變數內塞 JSON |
| 3 | Java System Properties | -Dserver.port=80 或 System.setProperty() |
| 4 | OS 環境變數 | SERVER_PORT=80 |
| 5 | jar 外的 application-{profile}.properties | 容器掛載 |
| 6 | jar 內的 application-{profile}.properties | 打包進去的 profile 檔 |
| 7 | jar 外的 application.properties | 容器掛載 |
| 8(最低) | jar 內的 application.properties | 打包進去的預設檔 |
記憶法:「越靠近運行時機越優先」、「外部檔案蓋過內部檔案」、「特定 profile 蓋過通用設定」。以下逐項說明。
最高優先:命令列參數
java Application --server.port=80
java -jar Application.jar --server.port=80
啟動 Java 程式時直接帶入的參數,會先傳到 Java 的程式進入點 main(String[] args),再傳到 SpringApplication.run。Spring Boot 把這個來源放在最高優先,方便我們在不改打包檔案的前提下臨時調整某個值。
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
以下寫法也有一樣效果,但這樣寫只是用來讓人更加清楚參數的傳遞,實際上可別這樣寫啊!
public static void main(String[] args) {
SpringApplication.run(Application.class, new String[]{"--server.port=80"});
}
第二優先:SPRING_APPLICATION_JSON
可以把整包 JSON 設定塞進名為 SPRING_APPLICATION_JSON 的環境變數,Spring Boot 啟動時會解析它。在 CI/CD 或容器化部署時,一次塞多個設定會比一個個環境變數乾淨。
export SPRING_APPLICATION_JSON='{"server":{"port":80},"logging":{"level":{"root":"INFO"}}}'
java -jar Application.jar
第三優先:Java System Properties
透過 -D 啟動參數或在程式內呼叫 System.setProperty(),本質上是同一個 Property Source(都是 Java 的 System Properties)。在 Spring Boot 的優先級表中算同一級,誰覆蓋誰只取決於誰後寫入。
java -Dserver.port=80 Application
java -Dserver.port=80 -jar Application.jar
等價於在程式內手動寫入:
public static void main(String[] args) {
System.setProperty("server.port", "80");
SpringApplication.run(Application.class, args);
}
注意 System.setProperty() 必須在 SpringApplication.run() 之前呼叫才會生效,否則 Spring 已經讀取完 System Properties 後再設定就太晚了。
第四優先:OS 環境變數
OS 環境變數是 Docker、Kubernetes、Cloud Run 等容器化部署最常用的方式,因為映像檔本身可以維持不變、只透過環境變數覆蓋特定設定。Spring Boot 會自動把環境變數的命名做轉換,例如 server.port 對應到 SERVER_PORT,規則是「點轉底線、全大寫」。
export SERVER_PORT=80
java -jar Application.jar
常見的對應規則:
| Property 名稱 | 環境變數名稱 |
|---|---|
| server.port | SERVER_PORT |
| spring.datasource.url | SPRING_DATASOURCE_URL |
| spring.profiles.active | SPRING_PROFILES_ACTIVE |
| logging.level.root | LOGGING_LEVEL_ROOT |
第五~第八優先:application.properties 與 application-{profile}.properties
設定檔的部分有兩個維度交叉:「特定 profile 還是通用」、「在 jar 外還是 jar 內」。Spring Boot 把這四種組合排成從高到低四級。
| 優先級 | 檔案 |
|---|---|
| 5 | jar 外的 application-{profile}.properties |
| 6 | jar 內的 application-{profile}.properties |
| 7 | jar 外的 application.properties |
| 8 | jar 內的 application.properties |
「jar 外」是指啟動時跟 jar 同目錄、或 ./config/ 資料夾下的檔案,Spring Boot 會自動掃描。這個設計讓我們能把通用設定打包進去,部署到不同環境時用外部檔案覆蓋特定值,不必每次重新打包。
# application.properties 與 application-{profile}.properties 寫法
server.port=80
# application.yml 與 application-{profile}.yml 寫法
server:
port: 80
關於 application-{profile} 的命名規則、SPRING_PROFILES_ACTIVE 用法、profile groups 等細節,請參考另一篇文章「指定 Spring Boot 執行環境的多種方式」。
其他來源
Spring Boot 官方文件列的 Property Source 還有更多,本文只挑常用的整理。如果有特殊需求可以參考完整列表:
@TestPropertySource:測試類別專用,優先級會比命令列參數還高- Devtools 全域設定:
~/.spring-boot-devtools.properties - JNDI attributes:傳統 Java EE 容器才會用到
- RandomValuePropertySource:用
${random.uuid}之類的語法產生隨機值 @PropertySource:在@Configuration類別上指定額外設定檔來源(優先級非常低)SpringApplication.setDefaultProperties():當所有來源都沒有時的預設值