在 Day08 內容裡,我們已經理解了 Properties 如同應用程式的「遙控器」,也認識了 Profiles 這個強大的多環境管理機制。現在,是時候學習如何將這些設定真正地「連接」到我們的 Java 程式碼中,讓應用程式根據我們的配置來運作。
今天將深入程式碼,掌握將配置注入到應用程式中的各種方法,並學習處理更複雜的配置場景與最佳實踐。
Spring Boot 提供了多種靈活的方式來讀取設定檔中的屬性。我們將介紹三種最主要的方法,從最簡單到功能最強大的逐一解析。
假設我們有以下的 application.properties
設定:
app.name=My Awesome App
app.version=1.0.0
app.description=這是一個很棒的應用程式!
@Value
— 注入單一值@Value
註解 (Annotation) 是最直接、最簡單的方式,適合用來讀取單一的屬性值。
使用方式:
您只需要在類別的屬性 (Field) 上方加上 @Value("${屬性名稱}")
即可。
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component // 標記為 Spring 的組件,讓 Spring 容器管理它
public class AppInfo {
@Value("${app.name}")
private String appName;
@Value("${app.version}")
private String appVersion;
// 您也可以在這裡使用預設值
@Value("${app.author:Default Author}")
private String appAuthor;
public void printAppInfo() {
System.out.println("應用程式名稱: " + appName);
System.out.println("應用程式版本: " + appVersion);
System.out.println("應用程式作者: " + appAuthor); // 如果設定檔沒有 app.author,會顯示 "Default Author"
}
}
@Value
註解 (Annotation),顯得雜亂且不易管理。Environment
抽象 — 動態存取Spring 的 Environment
是一個介面 (Interface),它代表了應用程式執行時的環境。您可以透過它以程式化的方式,動態地取得任何屬性值。
使用方式:
透過依賴注入 (Dependency Injection) 取得 Environment
物件,然後呼叫 getProperty()
方法。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Service;
@Service
public class MyDynamicService {
@Autowired
private Environment env;
public String getAppName() {
// 直接透過 key 來取得屬性值
return env.getProperty("app.name");
}
public String getAppDescription() {
// 也可以提供一個預設值
return env.getProperty("app.description", "這是一個預設的描述。");
}
}
getProperty()
回傳的都是字串 (String),您需要手動轉型,且如果屬性名稱拼錯,編譯器不會報錯,只會在執行時回傳 null
。@ConfigurationProperties
— 類型安全的結構化綁定 (👍 強烈推薦)這是 Spring Boot 最推薦的方式。@ConfigurationProperties
可以將一組相關的屬性,直接「綁定」到一個 Java 物件 (POJO) 上,實現類型安全且結構化的配置管理。
使用步驟:
步驟 1:建立一個設定類別 (Configuration Class)
這個類別的屬性 (Field) 名稱必須和設定檔中的 key 對應。
import org.springframework.boot.context.properties.ConfigurationProperties;
// 將所有以 "app" 為前綴的屬性綁定到這個類別上
@ConfigurationProperties(prefix = "app")
public class AppConfig {
private String name;
private String version;
private String description;
// 必須提供對應的 Getter 和 Setter 方法!
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getVersion() { return version; }
public void setVersion(String version) { this.version = version; }
public String getDescription() { return description; }
public void setDescription(String description) { this.description = description; }
}
注意:Spring Boot 會自動進行駝峰式 (camelCase) 和 Kebab-case 的轉換。例如,設定檔中的 app.welcome-message 會自動對應到 Java 類別中的 welcomeMessage 屬性 (Field)。
步驟 2:啟用這個設定類別
要讓 Spring Boot 知道這個類別的存在,您有兩種方式:
@Component
註解 (Annotation),讓它成為一個 Spring Bean。import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component // 讓 Spring 掃描並註冊這個 Bean
@ConfigurationProperties(prefix = "app")
public class AppConfig { /* ... 省略 ... */ }
@EnableConfigurationProperties
來明確指定要啟用的設定類別。import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
@SpringBootApplication
@EnableConfigurationProperties(AppConfig.class) // 明確啟用 AppConfig
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
步驟 3:在其他地方注入並使用
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class AppService {
private final AppConfig appConfig;
// 透過建構子注入,這是 Spring 推薦的最佳實踐
@Autowired
public AppService(AppConfig appConfig) {
this.appConfig = appConfig;
}
public void displayAppConfig() {
System.out.println("從 AppConfig 讀取到的名稱: " + appConfig.getName());
System.out.println("從 AppConfig 讀取到的版本: " + appConfig.getVersion());
}
}
abc
設給一個 Integer
屬性),應用程式啟動時會直接報錯。@ConfigurationProperties
有很好的支援,可以提供自動完成和語法檢查。@ConfigurationProperties
的強大之處在於它能輕鬆處理複雜的資料結構。
處理列表 (List)
假設您想設定一個支援的伺服器列表。
application.yml
(推薦的格式)
server-list:
hosts:
- "server1.example.com"
- "server2.example.com"
- "server3.example.com"
application.properties
(語法較繁瑣)
server-list.hosts[0]=server1.example.com
server-list.hosts[1]=server2.example.com
server-list.hosts[2]=server3.example.com
對應的 Java 設定類別
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.List;
@Component
@ConfigurationProperties(prefix = "server-list")
public class ServerListConfig {
private List<String> hosts;
public List<String> getHosts() { return hosts; }
public void setHosts(List<String> hosts) { this.hosts = hosts; }
}
Spring Boot 會自動將設定檔中的多個值,綁定到 List<String>
結構中。
現在,讓我們將 Profiles 的概念與實際程式碼結合起來。
場景:我們希望在開發環境 (dev
) 使用內嵌的 H2 資料庫方便快速啟動,而在正式環境 (prod
) 則連接到外部的 MySQL 資料庫。
步驟 1:建立多個設定檔
application.properties
(基礎設定)
# 預設啟用 dev 環境
spring.profiles.active=dev
app.name=My App
application-dev.properties
(開發環境專用)
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=password
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
application-prod.properties
(正式環境專用)
spring.datasource.url=jdbc:mysql://prod-db.example.com:3306/maindb
spring.datasource.driverClassName=com.mysql.cj.jdbc.Driver
spring.datasource.username=prod_user
spring.datasource.password=ToughPassword!
spring.jpa.database-platform=org.hibernate.dialect.MySQLDialect
步驟 2:在程式碼中根據 Profile 載入不同的 Bean
我們可以使用 @Profile
註解 (Annotation) 來告訴 Spring,某個 Bean 只在特定的 Profile 被啟用時才需要被建立。
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
// 一個設定類別的容器
@Configuration
public class DataSourceConfig {
@Profile("dev") // 只有在 dev profile 啟用時,這個 Bean 才被建立
public void devDatabaseBean() {
System.out.println("======================================");
System.out.println(" DEV 環境已啟用,使用 H2 資料庫");
System.out.println("======================================");
}
@Profile("prod") // 只有在 prod profile 啟用時,這個 Bean 才被建立
public void prodDatabaseBean() {
System.out.println("**************************************");
System.out.println(" PROD 環境已啟用,連接 MySQL 資料庫");
System.out.println("**************************************");
}
}
步驟 3:啟用不同的 Profile
application.properties
中設定了 spring.profiles.active=dev
,您會在控制台看到 "DEV 環境已啟用" 的訊息。prod
環境:java -jar myapp.jar --spring.profiles.active=prod
執行後,您會在控制台看到 "PROD 環境已啟用" 的訊息。命令列參數的優先級高於設定檔中的設定。
屬性覆寫的完整優先順序:請記住這個順序,當您發現設定不如預期時,這會是除錯的關鍵。
application-prod.properties
)application.properties
)大小寫敏感性:設定檔中的 key
(如 app.name
) 是大小寫不敏感的 (但建議統一小寫),而 @Value
中的佔位符 (Placeholders) ${app.name}
則是大小寫敏感的。
中文亂碼問題:請確保您所有的 .properties
或 .yml
檔案都使用 UTF-8
編碼儲存,以避免中文顯示為亂碼。
敏感資訊管理:絕對不要將資料庫密碼、API 金鑰等敏感資訊直接寫在設定檔並提交到版本控制系統 (如 Git)。最佳實踐是透過環境變數或專門的密鑰管理服務 (如 HashiCorp Vault, AWS Secrets Manager) 在部署時注入。