SpringBoot外部化配置的最佳實踐指南
總體理解:什么是“外部化配置”?
Externalized Configuration 的核心思想是:
把應(yīng)用程序的配置(如數(shù)據(jù)庫地址、端口號、日志級別等)從 Java 代碼中剝離出來,放在外部文件或環(huán)境中,這樣可以在不同環(huán)境(開發(fā)、測試、生產(chǎn))使用相同的代碼,但加載不同的配置。
Spring Boot 支持多種方式來實現(xiàn)配置的外部化:
.properties文件.yml/.yaml文件- 環(huán)境變量(Environment Variables)
- 命令行參數(shù)(Command-line arguments)
- JSON 字符串(通過系統(tǒng)屬性或環(huán)境變量注入)
- JNDI、Servlet 初始化參數(shù)等
這些配置最終都會被統(tǒng)一加載到 Spring 的 Environment 對象中,并可以注入到 Bean 中使用。
配置優(yōu)先級順序(PropertySource Order)
Spring Boot 有一套嚴(yán)格的配置加載順序,后加載的會覆蓋先加載的。以下是按優(yōu)先級從低到高排列的(即:后面的可以覆蓋前面的值):
| 優(yōu)先級 | 來源 |
|---|---|
| 1 | Devtools 全局設(shè)置($HOME/.config/spring-boot) |
| 2 | @TestPropertySource 注解(測試專用) |
| 3 | 測試注解上的 properties 屬性(如 @SpringBootTest(properties = {...})) |
| 4 | 命令行參數(shù)(如 --server.port=9000)? 最高優(yōu)先級之一 |
| 5 | SPRING_APPLICATION_JSON(內(nèi)聯(lián) JSON 字符串) |
| 6 | ServletConfig 初始化參數(shù) |
| 7 | ServletContext 初始化參數(shù) |
| 8 | JNDI 屬性 |
| 9 | JVM 系統(tǒng)屬性(System.getProperties()) |
| 10 | 操作系統(tǒng)環(huán)境變量 ? 常用于云環(huán)境 |
| 11 | random.* 隨機值生成器 |
| 12 | 外部 profile-specific 配置文件(如 application-dev.properties) |
| 13 | 內(nèi)部(jar 包內(nèi))profile-specific 配置文件 |
| 14 | 外部 application.properties |
| 15 | 內(nèi)部 application.properties |
| 16 | @PropertySource 注解(注意:加載較晚,不能影響早期配置) |
| 17 | 默認(rèn)屬性(通過 SpringApplication.setDefaultProperties() 設(shè)置) |
關(guān)鍵點:
- 外部配置 > 內(nèi)部配置
- Profile-specific 配置 > 普通配置
- 命令行參數(shù) > 大多數(shù)其他方式(非常強大)
@PropertySource加載時機較晚,不能用于控制日志、主類等早期配置
實例說明:@Value 注入配置
@Component
public class MyBean {
@Value("${name}")
private String name;
}
這個 name 的值可以從多個地方來:
- jar 包內(nèi)默認(rèn)值:
src/main/resources/application.properties中定義name=defaultName - 外部覆蓋:在 jar 包同目錄下放一個
application.properties,寫name=prodName→ 會被優(yōu)先加載 - 命令行指定:運行時加參數(shù)
java -jar app.jar --name="Spring"→ 最終值就是"Spring"
這就是“一套代碼,多環(huán)境部署”的基礎(chǔ)。
調(diào)試技巧:使用 /env 和 /configprops 端點
Spring Boot Actuator 提供了兩個重要端點用于診斷配置問題:
/actuator/env:查看當(dāng)前所有生效的配置及其來源/actuator/configprops:查看@ConfigurationProperties綁定的對象狀態(tài)
當(dāng)你發(fā)現(xiàn)某個配置沒生效時,可以用這兩個接口查清楚它到底從哪來的、有沒有被覆蓋。
支持通配符路徑(Wildcard Locations)
Spring Boot 支持配置目錄使用通配符,例如:
--spring.config.location=config/*/ # 加載所有 config 下的子目錄
使用場景:Kubernetes 配置掛載
在 Kubernetes 中,你可能把不同服務(wù)的配置分別掛載為 ConfigMap:
/config/redis/application.properties /config/mysql/application.properties
如果你設(shè)置 config/*/,Spring Boot 會自動掃描并合并這兩個文件中的配置。
注意:
- 通配符路徑必須以
/結(jié)尾(如config/*/) - 按文件絕對路徑字母排序加載
- 適用于目錄,不適用于單個文件
SPRING_APPLICATION_JSON:用 JSON 注入配置
你可以通過環(huán)境變量或系統(tǒng)屬性傳入 JSON 格式的配置:
方法一:環(huán)境變量(Unix/Linux)
$ SPRING_APPLICATION_JSON='{"acme":{"name":"test"}}' java -jar myapp.jar
等價于配置了:
acme.name=test
方法二:JVM 系統(tǒng)屬性
$ java -Dspring.application.json='{"name":"test"}' -jar myapp.jar
方法三:命令行參數(shù)
$ java -jar myapp.jar --spring.application.json='{"name":"test"}'
方法四:JNDI
java:comp/env/spring.application.json
注意:JSON 中的 null 值不會覆蓋低優(yōu)先級的已有配置(視為“缺失”而非“設(shè)為空”)
RandomValuePropertySource:生成隨機值
用于注入隨機數(shù),適合測試或生成密鑰:
my.secret=${random.value}
my.number=${random.int}
my.uuid=${random.uuid}
my.number.less.than.ten=${random.int(10)} # 0~9
my.number.in.range=${random.int[1024,65536]} # 1024~65535
命令行參數(shù)處理
默認(rèn)情況下,Spring Boot 會把 --xxx=yyy 這樣的參數(shù)轉(zhuǎn)為配置項加入 Environment。
例如:
java -jar app.jar --server.port=9000 --debug
等價于設(shè)置了:
server.port=9000 debug=true
命令行參數(shù)優(yōu)先級極高,常用于臨時調(diào)試或 CI/CD 動態(tài)配置。
如果你想禁用這個功能:
SpringApplication app = new SpringApplication(MyApp.class); app.setAddCommandLineProperties(false); // 禁用命令行參數(shù)解析 app.run(args);
application.properties 的加載位置
Spring Boot 會在以下位置查找 application.properties(按優(yōu)先級從高到低):
file:./config/(當(dāng)前項目根目錄下的 config 文件夾)file:./(當(dāng)前項目根目錄)classpath:/config/(類路徑下的 config 包)classpath:/(類路徑根目錄)
越靠近項目的外部配置,優(yōu)先級越高。
你可以自定義配置文件名:
java -jar myapp.jar --spring.config.name=myproject # 會去加載 myproject.properties 而不是 application.properties
也可以指定配置文件路徑:
java -jar myapp.jar --spring.config.location=classpath:/default.properties,classpath:/override.properties
spring.config.location vs spring.config.additional-location
| 參數(shù) | 行為 | 示例 |
|---|---|---|
| spring.config.location | 替換默認(rèn)位置 | --spring.config.location=custom/ → 只加載 custom/ 目錄 |
| spring.config.additional-location | 追加額外位置(先加載) | --spring.config.additional-location=custom/ → 先加載 custom/,再加載默認(rèn)位置 |
使用 additional-location 可以實現(xiàn)“默認(rèn)配置 + 局部覆蓋”的模式。
Profile-specific Properties(環(huán)境特定配置)
命名規(guī)則:application-{profile}.properties 或 application-{profile}.yml
比如:
application-dev.propertiesapplication-prod.yml
加載邏輯:
- 如果激活了
dev環(huán)境,則加載application-dev.properties - Profile-specific 文件總是覆蓋普通文件
- 多個 profile 激活時,后激活的優(yōu)先級更高(last-wins)
可以通過以下方式激活 profile:
--spring.profiles.active=dev,mysql
或者在 application.properties 中設(shè)置:
spring.profiles.active=dev
注意:
- 如果你用了
spring.config.location指定了具體文件,不會自動加載 profile 變體 - 推薦使用目錄形式,如
config/*/來支持 profile 文件
占位符(Placeholders)支持
可以在 .properties 文件中引用其他已定義的屬性:
app.name=MyApp
app.description=${app.name} is a Spring Boot application
結(jié)果:app.description = MyApp is a Spring Boot application
這在簡化長配置時很有用。
加密屬性(Encrypting Properties)
Spring Boot 本身不提供加密功能。
但你可以通過實現(xiàn) EnvironmentPostProcessor 接口,在應(yīng)用啟動前修改 Environment 中的屬性值,從而實現(xiàn)解密。
例如:
- 讀取加密的數(shù)據(jù)庫密碼
- 在
EnvironmentPostProcessor中調(diào)用解密算法 - 替換原始值為明文
推薦方案:
使用 Spring Cloud Vault 或 HashiCorp Vault 來集中管理敏感配置。
使用 YAML 替代 Properties
YAML 是 JSON 的超集,更適合表達層級結(jié)構(gòu)。
示例:
environments:
dev:
url: https://dev.example.com
name: Developer Setup
prod:
url: https://prod.example.com
name: Production
等價于:
environments.dev.url=https://dev.example.com environments.dev.name=Developer Setup environments.prod.url=https://prod.example.com environments.prod.name=Production
列表寫法:
my:
servers:
- dev.example.com
- prod.example.com
轉(zhuǎn)換為:
my.servers[0]=dev.example.com my.servers[1]=prod.example.com
要綁定到 Java 對象,需定義 List<String> 類型:
@ConfigurationProperties("my")
public class MyConfig {
private List<String> servers = new ArrayList<>();
// getter/setter
}
多 Profile 的 YAML 寫法(--- 分隔)
YAML 支持在一個文件中寫多個 profile 的配置:
server: address: 192.168.1.100 --- spring: profiles: development server: address: 127.0.0.1 --- spring: profiles: production & eu-central server: address: 192.168.1.120
- 用
---分隔不同文檔 - 每個文檔可以用
spring.profiles指定適用環(huán)境 - 支持表達式:
production & (eu-central | eu-west) - 支持取反:
!test表示“非 test 環(huán)境”
注意:不要混用 profile-specific 文件(如 application-dev.yml)和多文檔 YAML,否則嵌套文檔可能被忽略。
YAML 的局限性
- 不能用
@PropertySource注解加載 YAML 文件- 所以如果你必須用
@PropertySource,就只能用.properties文件
- 所以如果你必須用
- 在 profile-specific 的 YAML 文件中使用
---多文檔語法可能導(dǎo)致意外行為- 因為文件本身已經(jīng)是 profile-specific,內(nèi)部的
spring.profiles可能被忽略
- 因為文件本身已經(jīng)是 profile-specific,內(nèi)部的
建議:要么全用多文檔 YAML,要么全用 profile-specific 文件,不要混用
總結(jié):核心要點一覽
| 主題 | 關(guān)鍵結(jié)論 |
|---|---|
| 配置來源 | properties、yaml、環(huán)境變量、命令行、JSON、JNDI 等 |
| 優(yōu)先級順序 | 命令行 > 環(huán)境變量 > 外部文件 > 內(nèi)部文件 > 默認(rèn)值 |
| Profile 配置 | application-{profile}.xxx,優(yōu)先級高于普通配置 |
| YAML vs Properties | YAML 更適合復(fù)雜結(jié)構(gòu),Properties 更通用 |
| 通配符路徑 | config/*/ 可用于 Kubernetes 多 ConfigMap 場景 |
| 調(diào)試工具 | /actuator/env 查看所有配置來源 |
| 隨機值 | ${random.int}, ${random.uuid} |
| 占位符 | ${app.name} 引用其他屬性 |
| 加密 | 需自行實現(xiàn) EnvironmentPostProcessor 或用 Vault |
| 最佳實踐 | 外部配置 + profile + YAML + 命令行參數(shù)組合使用 |
實際開發(fā)建議
- 本地開發(fā):用
application-dev.properties+ IDE 運行參數(shù) - 測試環(huán)境:CI/CD 中通過
--spring.profiles.active=test激活 - 生產(chǎn)環(huán)境:
- 使用
--spring.config.location=file:/etc/myapp/config/指向外置目錄 - 敏感信息通過
SPRING_APPLICATION_JSON或 Vault 注入 - 用
--spring.profiles.active=prod激活生產(chǎn)配置
- 使用
- K8s 部署:
- 用 ConfigMap 掛載多個 YAML 文件到
config/*/ - 使用
spring.config.location=config/*/自動合并
- 用 ConfigMap 掛載多個 YAML 文件到
以上就是SpringBoot外部化配置的最佳實踐指南的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot外部化配置的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Mybatis-Plus中使用@DS注解動態(tài)選擇數(shù)據(jù)源的源碼解讀
這篇文章主要介紹了Mybatis-Plus中使用@DS注解動態(tài)選擇數(shù)據(jù)源的源碼解讀,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-07-07
java使用webuploader實現(xiàn)跨域上傳詳解
目前初步接觸JAVA圖片上傳,用的webuploader。已經(jīng)跟后臺對接上,但是有個問題就是跨域請求,通過查找相關(guān)資料終于實現(xiàn)了,下面這篇文章主要給大家介紹了關(guān)于java使用webuploader實現(xiàn)跨域上傳的相關(guān)資料,需要的朋友可以參考下。2017-07-07
java根據(jù)模板實現(xiàn)填充word內(nèi)容并轉(zhuǎn)換為pdf
這篇文章主要為大家詳細(xì)介紹了java如何根據(jù)模板實現(xiàn)填充word內(nèi)容并轉(zhuǎn)換為pdf,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2025-04-04
Maven中exec插件執(zhí)行Java程序的實現(xiàn)
在Maven項目中,可以使用Maven的插件來執(zhí)行Java程序,本文主要介紹了Maven中exec插件執(zhí)行Java程序的實現(xiàn),具有一定的參考價值,感興趣的可以了解一下2023-12-12
springboot定時任務(wù)備份mysql數(shù)據(jù)庫的實現(xiàn)示例
為了防止數(shù)據(jù)庫被清庫或者誤刪數(shù)據(jù)庫的情況,所以需要定時將mysql數(shù)據(jù)庫中的數(shù)據(jù)進行備份,本文主要介紹了springboot定時任務(wù)備份mysql數(shù)據(jù)庫的實現(xiàn)示例,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2024-03-03

