Spring Framework 是 Java 中的好框架,出了 Spring Boot 之後又讓方便度更上一層樓,只需要簡單的一點設定就可以開始使用。但同一個專案在不同環境有不同的需求,需要有不同的變數與設定值。因此可以建立多個 application.properties 設定檔(或是 application.yml),常見的會有開發版本(dev)、測試版本(test)、生產版本(prod)等,在此介紹指定環境的幾種方式。

設定檔的基本寫法

例如我們有以下的 application.properties 檔案,指定的資料庫的連結網址與帳號密碼。

spring.datasource.url = jdbc:mysql://...?useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8
spring.datasource.username = root
spring.datasource.password = root

或者也可以使用 application.yml 檔案來指定,YAML 透過縮排表達層級會比較好讀。

spring:
  datasource:
    url: "jdbc:mysql://...?useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8"
    username: "root"
    password: "root"

顯然生產環境與測試環境的資料庫帳號密碼通常不會是如此簡單的設置,而且資料庫連結也可能不同,因此我們可以建立好幾個設定檔案,檔案名稱與功能如下。

功能Properties 檔案名稱YAML 檔案名稱
通用版application.propertiesapplication.yml
開發版application-dev.propertiesapplication-dev.yml
測試版application-test.propertiesapplication-test.yml
生產版application-prod.propertiesapplication-prod.yml
設定檔案的名稱與功能

有一點值得注意的是,只有通用版的設定檔案需要完整的內容,其他版本只需要填寫不同的地方即可。例如每個版本的 server.port 都是 80,那麼只需要寫在通用版的設定檔案裡面。設定重複的時候,其他環境的設定會覆蓋通用版的內容。完整的覆蓋順序(包含命令列參數、環境變數、jar 內外檔案等)可以參考另一篇「實測 Spring Boot 中多個 Properties 設定的覆蓋順序」。

啟動時指定 Profile

使用環境變數

通常正式運作的環境會簡稱為 prod,因此希望運作的是 application-prod.properties 時,可以透過環境變數設定,以 Linux 為例在啟動腳本加上以下內容。

export SPRING_PROFILES_ACTIVE=prod

環境變數還可以用逗號同時啟用多個 profile,例如以下會同時讀取 application-prod、application-mysql、application-monitoring 三份設定,後面 profile 的值會覆蓋前面的。這在「環境+資料庫+功能」這種多軸組合的專案很實用,不需要為每種組合各做一份檔案。

export SPRING_PROFILES_ACTIVE=prod,mysql,monitoring

使用啟動參數

如果已經將 Spring Boot 專案打包成包含 Tomcat 的可執行 Jar 檔,可以用以下方式啟動來指定 prod。

java -jar (JAR檔案名稱) --spring.profiles.active=prod

Windows 環境變數設定

如果是 Windows 也可以在設定環境變數的視窗加上 SPRING_PROFILES_ACTIVE 來指定。

載入外部設定檔

除了讓 Spring 自動讀取 classpath 內的 application-{profile}.properties 之外,也可以在啟動 JAR 的時候指定額外的設定檔位置。Spring Boot 提供兩個常用參數,行為差很多,要小心區分。

spring.config.location(取代)

使用 spring.config.location 會「取代」Spring 預設的設定檔搜尋位置。也就是說,原本會自動讀取的 classpath 內的 application.properties 與 application-{profile}.properties 都不會被載入,必須在指定的位置寫好完整的設定內容,Spring 不會去其他地方找漏掉的值。

java -jar (JAR檔案名稱) --spring.config.location=/etc/myapp/application.yml

spring.config.additional-location(附加)

大多數情況下我們其實只想「補上」幾個值,不希望把預設位置整個換掉。Spring Boot 2.4 之後加入的 spring.config.additional-location 就是用來解決這個需求,它會在預設位置之外「附加」一個搜尋位置,外部設定檔的值會覆蓋預設值,但找不到的時候還是會去預設位置撈。

java -jar (JAR檔案名稱) --spring.config.additional-location=/etc/myapp/

spring.config.import(匯入)

Spring Boot 2.4 同時引入了更彈性的 spring.config.import,可以在設定檔內部宣告匯入其他來源,包含外部檔案、環境變數、Vault、Consul 等。例如以下寫法會在啟動時把 /etc/myapp/secret.properties 也匯入。

spring.config.import=optional:file:/etc/myapp/secret.properties

前面加 optional: 代表找不到也不會報錯,這對「開發環境沒這個檔案、正式環境才有」的情境很方便。

在程式內使用 Profile

@Profile 標註 Bean

除了切換設定檔,還可以在 Java 程式內透過 @Profile annotation 控制 Bean 在哪些 profile 才生效。例如下方範例的 Mock 版本只在 dev profile 啟用、真實版本只在 prod profile 啟用,避免開發環境意外打到正式 API。

import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service;

@Service
@Profile("dev")
public class MockPaymentService implements PaymentService { ... }

@Service
@Profile("prod")
public class RealPaymentService implements PaymentService { ... }

@Profile 也可以用 ! 表示「不在某 profile」,例如 @Profile("!prod") 代表除了 prod 以外都生效。

Profile Groups

Spring Boot 2.4 之後支援 profile group,可以把多個 profile 組成一個別名。例如以下設定讓啟用 production 時自動帶上 prod、db、monitoring 三個 profile,啟動指令也會更乾淨。

spring:
  profiles:
    group:
      production: "prod,db,monitoring"
      development: "dev,db,debug"
# 啟用 production 等於同時啟用 prod、db、monitoring
export SPRING_PROFILES_ACTIVE=production

單一檔案多 Profile(Multi-document Files)

Spring Boot 2.4 之後,properties 與 YAML 檔案都可以在同一份檔案內用 --- 分隔多個 profile 區塊,不一定要拆成多個檔案。對於設定差異不大的小專案,集中在一份檔案會比較好維護。以 YAML 為例:

server:
  port: 8080

---
spring:
  config:
    activate:
      on-profile: "dev"
spring.datasource.url: "jdbc:h2:mem:devdb"

---
spring:
  config:
    activate:
      on-profile: "prod"
spring.datasource.url: "jdbc:mysql://prod-db/app"

注意這邊用的是 spring.config.activate.on-profile,這是 Spring Boot 2.4 之後的新寫法。舊版的 spring.profiles 還能用但已經不推薦,新專案直接用新寫法即可。

版本相容性

本文提到的 spring.config.importspring.config.additional-locationspring.config.activate.on-profile、profile groups、multi-document files 等功能都是 Spring Boot 2.4 之後才有的,舊專案要先確認版本。如果還在 Spring Boot 2.3 或更早的版本,只能用 spring.config.locationspring.profiles.active 這兩個傳統做法。

另外 Spring Boot 3 從 2022-11 發布開始就要求 Java 17 以上,profile 相關行為跟 2.x 大致相容,但 javax.* 已全面換成 jakarta.*,舊專案升級時記得一併處理。