外部化配置
Spring Boot 允許你外部化你的配置,這樣你就可以在不同的環境中使用相同的應用程式程式碼。你可以使用各種外部配置源,包括 Java properties 檔案、YAML 檔案、環境變數和命令列引數。
屬性值可以透過使用 @Value 註解直接注入到你的 bean 中,透過 Spring 的 Environment 抽象來訪問,或者透過 @ConfigurationProperties 繫結到結構化物件。
Spring Boot 使用一種非常特殊的 PropertySource 順序,旨在允許合理地覆蓋值。後面的屬性源可以覆蓋前面定義的屬性值。
來源按以下順序考慮:
-
預設屬性(透過設定
SpringApplication.setDefaultProperties(Map)指定)。 -
你的
@Configuration類上的@PropertySource註解。請注意,這些屬性源在應用程式上下文重新整理之前不會新增到Environment中。這對於配置某些屬性(如logging.*和spring.main.*)來說太晚了,因為它們在重新整理開始之前就被讀取。 -
配置資料(例如
application.properties檔案)。 -
一個
RandomValuePropertySource,它只有random.*中的屬性。 -
作業系統環境變數。
-
Java 系統屬性(
System.getProperties())。 -
來自
java:comp/env的 JNDI 屬性。 -
ServletContext初始化引數。 -
ServletConfig初始化引數。 -
來自
SPRING_APPLICATION_JSON的屬性(嵌入在環境變數或系統屬性中的內聯 JSON)。 -
命令列引數。
-
你的測試上的
properties屬性。可在@SpringBootTest和 用於測試應用程式特定部分的測試註解 上使用。 -
你的測試中的
@DynamicPropertySource註解。 -
你的測試上的
@TestPropertySource註解。 -
當 devtools 啟用時,
$HOME/.config/spring-boot目錄中的 Devtools 全域性設定屬性。
配置檔案按以下順序考慮:
-
打包在你的 jar 中的 應用程式屬性(
application.properties和 YAML 變體)。 -
打包在你的 jar 中的 特定於配置檔案的應用程式屬性(
application-{profile}.properties和 YAML 變體)。 -
打包在你的 jar 之外的 應用程式屬性(
application.properties和 YAML 變體)。 -
打包在你的 jar 之外的 特定於配置檔案的應用程式屬性(
application-{profile}.properties和 YAML 變體)。
建議你的整個應用程式堅持使用一種格式。如果你在同一位置同時擁有 .properties 和 YAML 格式的配置檔案,則 .properties 優先。 |
如果你使用環境變數而不是系統屬性,大多數作業系統不允許使用句點分隔的鍵名,但你可以使用下劃線代替(例如,SPRING_CONFIG_NAME 而不是 spring.config.name)。詳情請參閱從環境變數繫結。 |
如果你的應用程式在 Servlet 容器或應用伺服器中執行,那麼可以使用 JNDI 屬性(在 java:comp/env 中)或 Servlet 上下文初始化引數,而不是或同時使用環境變數或系統屬性。 |
舉一個具體的例子,假設你開發了一個使用 name 屬性的 @Component,如以下示例所示
-
Java
-
Kotlin
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class MyBean {
@Value("${name}")
private String name;
// ...
}
import org.springframework.beans.factory.annotation.Value
import org.springframework.stereotype.Component
@Component
class MyBean {
@Value("\${name}")
private val name: String? = null
// ...
}
在你的應用程式類路徑中(例如,在你的 jar 內部),你可以有一個 application.properties 檔案,為 name 提供一個合理的預設屬性值。當在新環境中執行時,可以在你的 jar 外部提供一個 application.properties 檔案來覆蓋 name。對於一次性測試,你可以使用特定的命令列開關啟動(例如,java -jar app.jar --name="Spring")。
env 和 configprops 端點對於確定屬性為何具有特定值非常有用。你可以使用這兩個端點來診斷意外的屬性值。有關詳細資訊,請參閱生產就緒功能部分。 |
訪問命令列屬性
預設情況下,SpringApplication 會將任何命令列選項引數(即以 -- 開頭的引數,例如 --server.port=9000)轉換為 property 並將其新增到 Spring Environment。如前所述,命令列屬性始終優先於基於檔案的屬性源。
如果你不希望將命令列屬性新增到 Environment 中,你可以透過使用 SpringApplication.setAddCommandLineProperties(false) 來停用它們。
JSON 應用程式屬性
環境變數和系統屬性通常有嚴格的限制,這意味著有些屬性名稱無法使用。為了解決這個問題,Spring Boot 允許你將一個屬性塊編碼成一個 JSON 結構。
當你的應用程式啟動時,任何 spring.application.json 或 SPRING_APPLICATION_JSON 屬性將被解析並新增到 Environment 中。
例如,SPRING_APPLICATION_JSON 屬性可以在 UN*X shell 的命令列中作為環境變數提供
$ SPRING_APPLICATION_JSON='{"my":{"name":"test"}}' java -jar myapp.jar
在前面的示例中,你在 Spring Environment 中得到 my.name=test。
相同的 JSON 也可以作為系統屬性提供
$ java -Dspring.application.json='{"my":{"name":"test"}}' -jar myapp.jar
或者,你可以使用命令列引數提供 JSON
$ java -jar myapp.jar --spring.application.json='{"my":{"name":"test"}}'
如果你正在部署到經典的應用程式伺服器,你也可以使用名為 java:comp/env/spring.application.json 的 JNDI 變數。
儘管 JSON 中的 null 值將被新增到生成的屬性源中,但 PropertySourcesPropertyResolver 會將 null 屬性視為缺失值。這意味著 JSON 不能用 null 值覆蓋來自較低優先順序屬性源的屬性。 |
外部應用程式屬性
當你的應用程式啟動時,Spring Boot 會自動從以下位置查詢並載入 application.properties 和 application.yaml 檔案
-
從類路徑中
-
類路徑根目錄
-
類路徑
/config包
-
-
從當前目錄中
-
當前目錄
-
當前目錄中的
config/子目錄 -
config/子目錄的直接子目錄
-
列表按優先順序排序(後面項的值會覆蓋前面項的值)。從載入的檔案中提取的文件將作為 PropertySource 例項新增到 Spring Environment 中。
如果你不喜歡將 application 作為配置檔名,你可以透過指定 spring.config.name 環境變數來切換到另一個檔名。例如,要查詢 myproject.properties 和 myproject.yaml 檔案,你可以如下執行你的應用程式
$ java -jar myproject.jar --spring.config.name=myproject
你還可以透過使用 spring.config.location 環境變數來引用一個顯式位置。此屬性接受一個逗號分隔的一個或多個位置列表以進行檢查。
以下示例顯示瞭如何指定兩個不同的檔案
$ java -jar myproject.jar --spring.config.location=\
optional:classpath:/default.properties,\
optional:classpath:/override.properties
如果位置是可選的並且你不介意它們不存在,則使用字首 optional:。 |
spring.config.name、spring.config.location 和 spring.config.additional-location 在很早的時候就被用於確定要載入哪些檔案。它們必須定義為環境變數(通常是 OS 環境變數、系統屬性或命令列引數)。 |
如果 spring.config.location 包含目錄(而不是檔案),它們應該以 / 結尾。在執行時,它們將在載入之前附加從 spring.config.name 生成的名稱。在 spring.config.location 中指定的檔案將直接匯入。
目錄和檔案位置值也會展開以檢查特定於配置檔案的檔案。例如,如果你的 spring.config.location 是 classpath:myconfig.properties,你還會發現相應的 classpath:myconfig-<profile>.properties 檔案也會被載入。 |
在大多數情況下,你新增的每個 spring.config.location 項都將引用單個檔案或目錄。位置按其定義的順序處理,後面的位置可以覆蓋前面位置的值。
如果你有一個複雜的 konum 設定,並且使用了特定於配置檔案的配置檔案,你可能需要提供進一步的提示,以便 Spring Boot 知道它們應該如何分組。位置組是所有被視為同一級別的位置的集合。例如,你可能希望對所有類路徑位置進行分組,然後對所有外部位置進行分組。位置組中的項應該用 ; 分隔。有關詳細資訊,請參閱特定於配置檔案的檔案部分中的示例。
使用 spring.config.location 配置的位置會替換預設位置。例如,如果 spring.config.location 配置為 optional:classpath:/custom-config/,optional:file:./custom-config/,則考慮的完整位置集為
-
optional:classpath:custom-config/ -
optional:file:./custom-config/
如果你更喜歡新增額外的位置而不是替換它們,你可以使用 spring.config.additional-location。從額外位置載入的屬性可以覆蓋預設位置中的屬性。例如,如果 spring.config.additional-location 配置為 optional:classpath:/custom-config/,optional:file:./custom-config/,則考慮的完整位置集為
-
optional:classpath:/;optional:classpath:/config/ -
optional:file:./;optional:file:./config/;optional:file:./config/*/ -
optional:classpath:custom-config/ -
optional:file:./custom-config/
這種搜尋順序允許你在一個配置檔案中指定預設值,然後在另一個檔案中選擇性地覆蓋這些值。你可以在預設位置之一的 application.properties(或你使用 spring.config.name 選擇的任何其他基本名稱)中為你的應用程式提供預設值。然後,這些預設值可以在執行時透過位於自定義位置之一的不同檔案來覆蓋。
可選位置
預設情況下,當指定的配置資料位置不存在時,Spring Boot 會丟擲 ConfigDataLocationNotFoundException,你的應用程式將無法啟動。
如果你想指定一個位置,但你不介意它不總是存在,你可以使用 optional: 字首。你可以在 spring.config.location 和 spring.config.additional-location 屬性以及 spring.config.import 宣告中使用此字首。
例如,optional:file:./myconfig.properties 的 spring.config.import 值允許你的應用程式啟動,即使 myconfig.properties 檔案缺失。
如果你想忽略所有 ConfigDataLocationNotFoundException 錯誤並始終繼續啟動應用程式,你可以使用 spring.config.on-not-found 屬性。使用 SpringApplication.setDefaultProperties(…) 或使用系統/環境變數將其值設定為 ignore。
萬用字元位置
如果配置檔案位置的最後一個路徑段包含 * 字元,則它被視為萬用字元位置。載入配置時會展開萬用字元,以便也檢查直接子目錄。萬用字元位置在 Kubernetes 等環境中特別有用,因為存在多個配置屬性源。
例如,如果你有一些 Redis 配置和一些 MySQL 配置,你可能希望將這兩個配置塊分開,同時要求它們都存在於 application.properties 檔案中。這可能會導致兩個單獨的 application.properties 檔案掛載在不同的位置,例如 /config/redis/application.properties 和 /config/mysql/application.properties。在這種情況下,具有 config/*/ 的萬用字元位置將導致這兩個檔案都被處理。
預設情況下,Spring Boot 在預設搜尋位置中包含 config/*/。這意味著將搜尋你的 jar 之外的 /config 目錄的所有子目錄。
你可以在 spring.config.location 和 spring.config.additional-location 屬性中自己使用萬用字元位置。
萬用字元位置必須只包含一個 *,並且對於目錄搜尋位置以 */ 結尾,對於檔案搜尋位置以 */<filename> 結尾。包含萬用字元的位置按檔名的絕對路徑按字母順序排序。 |
萬用字元位置僅適用於外部目錄。你不能在 classpath: 位置中使用萬用字元。 |
特定於配置檔案的檔案
除了 application 屬性檔案之外,Spring Boot 還會嘗試使用命名約定 application-{profile} 來載入特定於配置檔案的檔案。例如,如果你的應用程式激活了一個名為 prod 的配置檔案並使用 YAML 檔案,那麼 application.yaml 和 application-prod.yaml 都將被考慮。
特定於配置檔案的屬性從與標準 application.properties 相同的位置載入,特定於配置檔案的檔案總是覆蓋非特定檔案。如果指定了多個配置檔案,則採用“後勝”策略。例如,如果 spring.profiles.active 屬性指定了 prod,live 配置檔案,則 application-prod.properties 中的值可能會被 application-live.properties 中的值覆蓋。
|
“後勝”策略適用於位置組級別。 例如,繼續上面的 /cfg application-live.properties /ext application-live.properties application-prod.properties 當我們的
當我們改為使用
|
如果未設定活動配置檔案,Environment 將有一組預設配置檔案(預設情況下為 [default])。換句話說,如果沒有明確啟用配置檔案,則會考慮 application-default 中的屬性。
| 屬性檔案只會載入一次。如果你已經直接匯入了一個特定於配置檔案的屬性檔案,那麼它將不會第二次匯入。 |
匯入額外資料
應用程式屬性可以使用 spring.config.import 屬性從其他位置匯入更多配置資料。匯入會在發現時進行處理,並被視為緊接在宣告匯入的文件下方插入的額外文件。
例如,你的類路徑 application.properties 檔案中可能有以下內容
-
屬性
-
YAML
spring.application.name=myapp
spring.config.import=optional:file:./dev.properties
spring:
application:
name: "myapp"
config:
import: "optional:file:./dev.properties"
這將觸發匯入當前目錄中的 dev.properties 檔案(如果此類檔案存在)。從匯入的 dev.properties 中獲取的值將優先於觸發匯入的檔案。在上面的示例中,dev.properties 可以將 spring.application.name 重新定義為不同的值。
無論宣告多少次,匯入只會匯入一次。
使用“固定”和“匯入相對”位置
匯入可以指定為固定或匯入相對位置。固定位置總是解析為相同的底層資源,無論 spring.config.import 屬性在哪裡宣告。匯入相對位置解析為相對於宣告 spring.config.import 屬性的檔案。
以斜槓 (/) 或 URL 樣式字首 (file:, classpath: 等) 開頭的位置被認為是固定的。所有其他位置都被認為是匯入相對的。
在確定位置是固定還是匯入相對時,不考慮 optional: 字首。 |
舉個例子,假設我們有一個 /demo 目錄,其中包含我們的 application.jar 檔案。我們可能會新增一個包含以下內容的 /demo/application.properties 檔案
spring.config.import=optional:core/core.properties
這是一個匯入相對位置,因此如果檔案 /demo/core/core.properties 存在,它將嘗試載入該檔案。
如果 /demo/core/core.properties 包含以下內容
spring.config.import=optional:extra/extra.properties
它將嘗試載入 /demo/core/extra/extra.properties。optional:extra/extra.properties 相對於 /demo/core/core.properties,因此完整目錄是 /demo/core/ + extra/extra.properties。
屬性順序
在屬性/yaml 檔案中單個文件內定義匯入的順序無關緊要。例如,以下兩個示例產生相同的結果
-
屬性
-
YAML
spring.config.import=my.properties
my.property=value
spring:
config:
import: "my.properties"
my:
property: "value"
-
屬性
-
YAML
my.property=value
spring.config.import=my.properties
my:
property: "value"
spring:
config:
import: "my.properties"
在上述兩個示例中,my.properties 檔案中的值將優先於觸發其匯入的檔案。
可以在單個 spring.config.import 鍵下指定多個位置。位置將按其定義的順序進行處理,後匯入的優先順序更高。
在適當的情況下,還會考慮特定於配置檔案的變體以進行匯入。上面的示例將匯入 my.properties 以及任何 my-<profile>.properties 變體。 |
|
Spring Boot 包含可插拔 API,允許支援各種不同的位置地址。預設情況下,你可以匯入 Java Properties、YAML 和配置樹。 第三方 jar 可以為其他技術提供支援(檔案不必是本地的)。例如,你可以想象配置資料來自外部儲存,例如 Consul、Apache ZooKeeper 或 Netflix Archaius。 如果你想支援自己的位置,請參閱 |
匯入無副檔名的檔案
一些雲平臺無法為卷掛載的檔案新增副檔名。為了匯入這些無副檔名的檔案,你需要給 Spring Boot 一個提示,以便它知道如何載入它們。你可以透過在方括號中放置一個副檔名提示來實現這一點。
例如,假設你有一個 /etc/config/myconfig 檔案,你想將其作為 yaml 匯入。你可以從 application.properties 中使用以下內容匯入它
-
屬性
-
YAML
spring.config.import=file:/etc/config/myconfig[.yaml]
spring:
config:
import: "file:/etc/config/myconfig[.yaml]"
使用環境變數
在雲平臺(如 Kubernetes)上執行應用程式時,你通常需要讀取平臺提供的配置值。你可以為此目的使用環境變數,或者使用配置樹。
你甚至可以將整個配置以屬性或 yaml 格式儲存在(多行)環境變數中,並使用 env: 字首載入它們。假設有一個名為 MY_CONFIGURATION 的環境變數,其內容如下
my.name=Service1
my.cluster=Cluster1
使用 env: 字首可以從這個變數中匯入所有屬性
-
屬性
-
YAML
spring.config.import=env:MY_CONFIGURATION
spring:
config:
import: "env:MY_CONFIGURATION"
此功能還支援指定副檔名。預設副檔名為 .properties。 |
使用配置樹
將配置值儲存在環境變數中存在缺點,特別是當值需要保密時。
作為環境變數的替代方案,許多雲平臺現在允許你將配置對映到掛載的資料卷中。例如,Kubernetes 可以卷掛載 ConfigMaps 和 Secrets。
有兩種常見的卷掛載模式可以使用
-
單個檔案包含一整套屬性(通常以 YAML 格式編寫)。
-
多個檔案寫入目錄樹,檔名成為“鍵”,內容成為“值”。
對於第一種情況,你可以直接使用 spring.config.import 匯入 YAML 或 Properties 檔案,如上文所述。對於第二種情況,你需要使用 configtree: 字首,以便 Spring Boot 知道它需要將所有檔案公開為屬性。
例如,假設 Kubernetes 掛載了以下卷
etc/
config/
myapp/
username
password
username 檔案的內容將是配置值,而 password 的內容將是秘密。
要匯入這些屬性,你可以將以下內容新增到你的 application.properties 或 application.yaml 檔案中
-
屬性
-
YAML
spring.config.import=optional:configtree:/etc/config/
spring:
config:
import: "optional:configtree:/etc/config/"
然後,你可以像往常一樣從 Environment 訪問或注入 myapp.username 和 myapp.password 屬性。
配置樹下的資料夾和檔案的名稱構成了屬性名。在上面的例子中,要將屬性作為 username 和 password 訪問,你可以將 spring.config.import 設定為 optional:configtree:/etc/config/myapp。 |
帶點符號的檔名也能正確對映。例如,在上面的例子中,/etc/config 中名為 myapp.username 的檔案將在 Environment 中產生 myapp.username 屬性。 |
配置樹值可以繫結到字串 String 和 byte[] 型別,具體取決於預期的內容。 |
如果你有多個配置樹要從同一個父資料夾匯入,你可以使用萬用字元快捷方式。任何以 /*/ 結尾的 configtree: 位置都將把所有直接子項作為配置樹匯入。與非萬用字元匯入一樣,每個配置樹下資料夾和檔案的名稱構成了屬性名。
例如,給定以下卷
etc/
config/
dbconfig/
db/
username
password
mqconfig/
mq/
username
password
你可以使用 configtree:/etc/config/*/ 作為匯入位置
-
屬性
-
YAML
spring.config.import=optional:configtree:/etc/config/*/
spring:
config:
import: "optional:configtree:/etc/config/*/"
這將新增 db.username、db.password、mq.username 和 mq.password 屬性。
| 使用萬用字元載入的目錄按字母順序排序。如果你需要不同的順序,那麼你應該將每個位置列為單獨的匯入 |
配置樹也可用於 Docker 金鑰。當 Docker swarm 服務被授予訪問金鑰的許可權時,金鑰會被掛載到容器中。例如,如果名為 db.password 的金鑰掛載在 /run/secrets/ 位置,你可以使用以下方法使 db.password 對 Spring 環境可用
-
屬性
-
YAML
spring.config.import=optional:configtree:/run/secrets/
spring:
config:
import: "optional:configtree:/run/secrets/"
屬性佔位符
application.properties 和 application.yaml 中的值在使用時會透過現有 Environment 進行過濾,因此你可以引用以前定義的值(例如,來自系統屬性或環境變數)。標準的 ${name} 屬性佔位符語法可以在值的任何地方使用。屬性佔位符還可以使用 : 分隔符來指定預設值,例如 ${name:default}。
以下示例顯示了使用帶和不帶預設值的佔位符的情況
-
屬性
-
YAML
app.name=MyApp
app.description=${app.name} is a Spring Boot application written by ${username:Unknown}
app:
name: "MyApp"
description: "${app.name} is a Spring Boot application written by ${username:Unknown}"
假設 username 屬性沒有在其他地方設定,則 app.description 的值將為 MyApp is a Spring Boot application written by Unknown。
|
你應該始終以其規範形式(使用小寫字母的烤肉串式)引用佔位符中的屬性名稱。這將允許 Spring Boot 使用與 寬鬆繫結 例如, |
| 你也可以使用此技術建立現有 Spring Boot 屬性的“短”變體。有關詳細資訊,請參閱“操作指南”中的使用“短”命令列引數部分。 |
處理多文件檔案
Spring Boot 允許你將一個物理檔案分割成多個邏輯文件,每個文件都獨立新增。文件按從上到下的順序處理。後面的文件可以覆蓋前面文件中定義的屬性。
對於 application.yaml 檔案,使用標準的 YAML 多文件語法。三個連續的連字元表示一個文件的結束,以及下一個文件的開始。
例如,以下檔案有兩個邏輯文件
spring:
application:
name: "MyApp"
---
spring:
application:
name: "MyCloudApp"
config:
activate:
on-cloud-platform: "kubernetes"
對於 application.properties 檔案,使用特殊的 #--- 或 !--- 註釋來標記文件分隔符
spring.application.name=MyApp
#---
spring.application.name=MyCloudApp
spring.config.activate.on-cloud-platform=kubernetes
| 屬性檔案分隔符不得有任何前導空格,並且必須正好有三個連字元。分隔符之前和之後的行不得具有相同的註釋字首。 |
多文件屬性檔案通常與啟用屬性(例如 spring.config.activate.on-profile)結合使用。有關詳細資訊,請參閱下一節。 |
多文件屬性檔案不能透過使用 @PropertySource 或 @TestPropertySource 註解載入。 |
啟用屬性
在滿足特定條件時,有時只啟用一組給定的屬性很有用。例如,你可能有一些屬性僅在特定配置檔案處於活動狀態時才相關。
你可以使用 spring.config.activate.* 條件性地啟用屬性文件。
以下啟用屬性可用
| 財產 | 備註 |
|---|---|
|
必須匹配才能使文件處於活動狀態的配置檔案表示式,或者必須至少匹配其中一個才能使文件處於活動狀態的配置檔案表示式列表。 |
|
必須檢測到才能使文件處於活動狀態的 |
例如,以下內容指定第二個文件僅在 Kubernetes 上執行且同時“prod”或“staging”配置檔案處於活動狀態時才處於活動狀態
-
屬性
-
YAML
myprop=always-set
#---
spring.config.activate.on-cloud-platform=kubernetes
spring.config.activate.on-profile=prod | staging
myotherprop=sometimes-set
myprop:
"always-set"
---
spring:
config:
activate:
on-cloud-platform: "kubernetes"
on-profile: "prod | staging"
myotherprop: "sometimes-set"
加密屬性
Spring Boot 不提供任何內建支援來加密屬性值,但是,它提供了必要的掛鉤點來修改 Spring Environment 中包含的值。EnvironmentPostProcessor 介面允許你在應用程式啟動之前操作 Environment。有關詳細資訊,請參閱在應用程式啟動之前自定義環境或應用程式上下文。
如果你需要一種安全的方式來儲存憑據和密碼,Spring Cloud Vault 專案提供了對將外部化配置儲存在 HashiCorp Vault 中的支援。
使用 YAML
YAML 是 JSON 的超集,因此是指定分層配置資料的便捷格式。只要你的類路徑中包含 SnakeYAML 庫,SpringApplication 類就會自動支援 YAML 作為屬性的替代方案。
如果你使用 starter,SnakeYAML 會由 spring-boot-starter 自動提供。 |
將 YAML 對映到屬性
YAML 文件需要從其分層格式轉換為扁平結構,以便與 Spring Environment 一起使用。例如,考慮以下 YAML 文件
environments:
dev:
url: "https://dev.example.com"
name: "Developer Setup"
prod:
url: "https://another.example.com"
name: "My Cool App"
為了從 Environment 訪問這些屬性,它們將被扁平化,如下所示
environments.dev.url=https://dev.example.com
environments.dev.name=Developer Setup
environments.prod.url=https://another.example.com
environments.prod.name=My Cool App
同樣,YAML 列表也需要扁平化。它們表示為帶有 [index] 解引用器的屬性鍵。例如,考慮以下 YAML
my:
servers:
- "dev.example.com"
- "another.example.com"
前面的示例將被轉換為這些屬性
my.servers[0]=dev.example.com
my.servers[1]=another.example.com
YAML 檔案不能透過使用 @PropertySource 或 @TestPropertySource 註解載入。因此,如果你需要以這種方式載入值,則需要使用屬性檔案。 |
直接載入 YAML
Spring Framework 提供了兩個方便的類,可用於載入 YAML 文件。YamlPropertiesFactoryBean 將 YAML 作為 Properties 載入,而 YamlMapFactoryBean 將 YAML 作為 Map 載入。
如果你想將 YAML 作為 Spring PropertySource 載入,你也可以使用 YamlPropertySourceLoader 類。
配置隨機值
RandomValuePropertySource 對於注入隨機值(例如,到秘密或測試用例中)很有用。它可以生成整數、長整數、UUID 或字串,如以下示例所示
-
屬性
-
YAML
my.secret=${random.value}
my.number=${random.int}
my.bignumber=${random.long}
my.uuid=${random.uuid}
my.number-less-than-ten=${random.int(10)}
my.number-in-range=${random.int[1024,65536]}
my:
secret: "${random.value}"
number: "${random.int}"
bignumber: "${random.long}"
uuid: "${random.uuid}"
number-less-than-ten: "${random.int(10)}"
number-in-range: "${random.int[1024,65536]}"
random.int* 語法是 OPEN value (,max) CLOSE,其中 OPEN,CLOSE 是任意字元,value,max 是整數。如果提供了 max,則 value 是最小值,max 是最大值(不包括)。
配置系統環境變數
Spring Boot 支援為環境屬性設定字首。如果系統環境由多個具有不同配置需求的 Spring Boot 應用程式共享,這會很有用。系統環境屬性的字首可以在 SpringApplication 上直接設定,方法是在應用程式執行之前呼叫 setEnvironmentPrefix(…) 方法。
例如,如果將字首設定為 input,則 remote.timeout 等屬性將在系統環境中解析為 INPUT_REMOTE_TIMEOUT。
此字首僅適用於系統環境變數。上述示例在從其他源讀取屬性時將繼續使用 remote.timeout。 |
型別安全配置屬性
使用 @Value("${property}") 註解注入配置屬性有時可能很麻煩,特別是當你處理多個屬性或資料本質上是分層的時候。Spring Boot 提供了一種處理屬性的替代方法,允許強型別 bean 管理和驗證你的應用程式的配置。
JavaBean 屬性繫結
可以繫結宣告標準 JavaBean 屬性的 bean,如以下示例所示
-
Java
-
Kotlin
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties("my.service")
public class MyProperties {
private boolean enabled;
private InetAddress remoteAddress;
private final Security security = new Security();
// getters / setters...
public boolean isEnabled() {
return this.enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public InetAddress getRemoteAddress() {
return this.remoteAddress;
}
public void setRemoteAddress(InetAddress remoteAddress) {
this.remoteAddress = remoteAddress;
}
public Security getSecurity() {
return this.security;
}
public static class Security {
private String username;
private String password;
private List<String> roles = new ArrayList<>(Collections.singleton("USER"));
// getters / setters...
public String getUsername() {
return this.username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return this.password;
}
public void setPassword(String password) {
this.password = password;
}
public List<String> getRoles() {
return this.roles;
}
public void setRoles(List<String> roles) {
this.roles = roles;
}
}
}
import org.springframework.boot.context.properties.ConfigurationProperties
import java.net.InetAddress
@ConfigurationProperties("my.service")
class MyProperties {
var isEnabled = false
var remoteAddress: InetAddress? = null
val security = Security()
class Security {
var username: String? = null
var password: String? = null
var roles: List<String> = ArrayList(setOf("USER"))
}
}
前面的 POJO 定義了以下屬性
-
my.service.enabled,預設值為false。 -
my.service.remote-address,其型別可以從String強制轉換。 -
my.service.security.username,帶有一個巢狀的“security”物件,其名稱由屬性名稱決定。特別是,型別在那裡根本沒有使用,並且可以是SecurityProperties。 -
my.service.security.password. -
my.service.security.roles,一個String集合,預設為USER。
要在屬性名稱中使用保留關鍵字,例如 my.service.import,請在該屬性的欄位上使用 @Name 註解。 |
對映到 Spring Boot 中可用的 @ConfigurationProperties 類的屬性(透過屬性檔案、YAML 檔案、環境變數和其他機制配置)是公共 API,但類本身的訪問器(getter/setter)不應直接使用。 |
|
這種安排依賴於預設的空建構函式,並且 getter 和 setter 通常是強制性的,因為繫結是透過標準的 Java Beans 屬性描述符進行的,就像在 Spring MVC 中一樣。在以下情況下可以省略 setter
有些人使用 Project Lombok 自動新增 getter 和 setter。請確保 Lombok 不會為此類型別生成任何特定建構函式,因為它會自動被容器用於例項化物件。 最後,只考慮標準的 Java Bean 屬性,不支援靜態屬性的繫結。 |
建構函式繫結
上一節中的示例可以以不可變的方式重寫,如以下示例所示
-
Java
-
Kotlin
import java.net.InetAddress;
import java.util.List;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.bind.DefaultValue;
@ConfigurationProperties("my.service")
public class MyProperties {
// fields...
private final boolean enabled;
private final InetAddress remoteAddress;
private final Security security;
public MyProperties(boolean enabled, InetAddress remoteAddress, Security security) {
this.enabled = enabled;
this.remoteAddress = remoteAddress;
this.security = security;
}
// getters...
public boolean isEnabled() {
return this.enabled;
}
public InetAddress getRemoteAddress() {
return this.remoteAddress;
}
public Security getSecurity() {
return this.security;
}
public static class Security {
// fields...
private final String username;
private final String password;
private final List<String> roles;
public Security(String username, String password, @DefaultValue("USER") List<String> roles) {
this.username = username;
this.password = password;
this.roles = roles;
}
// getters...
public String getUsername() {
return this.username;
}
public String getPassword() {
return this.password;
}
public List<String> getRoles() {
return this.roles;
}
}
}
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.boot.context.properties.bind.DefaultValue
import java.net.InetAddress
@ConfigurationProperties("my.service")
class MyProperties(val enabled: Boolean, val remoteAddress: InetAddress,
val security: Security) {
class Security(val username: String, val password: String,
@param:DefaultValue("USER") val roles: List<String>)
}
在這種設定中,存在單個引數化建構函式意味著應該使用建構函式繫結。這意味著繫結器將找到一個帶有你希望繫結的引數的建構函式。如果你的類有多個建構函式,可以使用 @ConstructorBinding 註解來指定用於建構函式繫結的建構函式。
要選擇退出類的建構函式繫結,引數化建構函式必須用 @Autowired 註解或設為 private。Kotlin 開發人員可以使用空的主建構函式來選擇退出建構函式繫結。
例如:
-
Java
-
Kotlin
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties("my")
public class MyProperties {
// fields...
final MyBean myBean;
private String name;
@Autowired
public MyProperties(MyBean myBean) {
this.myBean = myBean;
}
// getters / setters...
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
import org.springframework.boot.context.properties.ConfigurationProperties
@ConfigurationProperties("my")
class MyProperties() {
constructor(name: String) : this() {
this.name = name
}
// vars...
var name: String? = null
}
建構函式繫結可用於記錄。除非你的記錄有多個建構函式,否則無需使用 @ConstructorBinding。
建構函式繫結類的巢狀成員(如上面示例中的 Security)也將透過其建構函式進行繫結。
預設值可以使用建構函式引數和記錄元件上的 @DefaultValue 來指定。轉換服務將應用於將註解的 String 值強制轉換為缺失屬性的目標型別。
參考前面的例子,如果沒有屬性繫結到 Security,MyProperties 例項將包含 security 的 null 值。為了使其包含非空 Security 例項,即使沒有屬性繫結到它(在使用 Kotlin 時,這將要求 Security 的 username 和 password 引數宣告為可空,因為它們沒有預設值),請使用空的 @DefaultValue 註解
-
Java
-
Kotlin
public MyProperties(boolean enabled, InetAddress remoteAddress, @DefaultValue Security security) {
this.enabled = enabled;
this.remoteAddress = remoteAddress;
this.security = security;
}
class MyProperties(val enabled: Boolean, val remoteAddress: InetAddress,
@DefaultValue val security: Security) {
class Security(val username: String?, val password: String?,
@param:DefaultValue("USER") val roles: List<String>)
}
要使用建構函式繫結,該類必須透過 @EnableConfigurationProperties 或配置屬性掃描啟用。你不能將建構函式繫結與透過常規 Spring 機制建立的 bean(例如 @Component bean、透過使用 @Bean 方法建立的 bean 或透過使用 @Import 載入的 bean)一起使用 |
要使用建構函式繫結,該類必須使用 -parameters 進行編譯。如果你使用 Spring Boot 的 Gradle 外掛或使用 Maven 和 spring-boot-starter-parent,這將自動發生。 |
不建議將 Optional 與 @ConfigurationProperties 一起使用,因為它主要旨在用作返回型別。因此,它不適合配置屬性注入。為了與其他型別的屬性保持一致,如果你確實聲明瞭一個 Optional 屬性但它沒有值,則將繫結 null 而不是空的 Optional。 |
要在屬性名稱中使用保留關鍵字,例如 my.service.import,請在建構函式引數上使用 @Name 註解。 |
啟用 @ConfigurationProperties 註解的型別
Spring Boot 提供了繫結 @ConfigurationProperties 型別並將其註冊為 bean 的基礎設施。你可以逐類啟用配置屬性,或啟用與元件掃描類似地工作的配置屬性掃描。
有時,用 @ConfigurationProperties 註解的類可能不適合掃描,例如,如果你正在開發自己的自動配置或想有條件地啟用它們。在這些情況下,使用 @EnableConfigurationProperties 註解指定要處理的型別列表。這可以在任何 @Configuration 類上完成,如以下示例所示
-
Java
-
Kotlin
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(SomeProperties.class)
public class MyConfiguration {
}
import org.springframework.boot.context.properties.EnableConfigurationProperties
import org.springframework.context.annotation.Configuration
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(SomeProperties::class)
class MyConfiguration
-
Java
-
Kotlin
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties("some.properties")
public class SomeProperties {
}
import org.springframework.boot.context.properties.ConfigurationProperties
@ConfigurationProperties("some.properties")
class SomeProperties
要使用配置屬性掃描,請將 @ConfigurationPropertiesScan 註解新增到你的應用程式中。通常,它新增到用 @SpringBootApplication 註解的主應用程式類中,但也可以新增到任何 @Configuration 類中。預設情況下,掃描將從宣告該註解的類的包開始。如果你想定義要掃描的特定包,可以像以下示例所示那樣操作
-
Java
-
Kotlin
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
@SpringBootApplication
@ConfigurationPropertiesScan({ "com.example.app", "com.example.another" })
public class MyApplication {
}
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.context.properties.ConfigurationPropertiesScan
@SpringBootApplication
@ConfigurationPropertiesScan("com.example.app", "com.example.another")
class MyApplication
|
當使用配置屬性掃描或透過 假設它在 |
我們建議 @ConfigurationProperties 只處理環境,特別是不要從上下文中注入其他 bean。對於特殊情況,可以使用 setter 注入或框架提供的任何 *Aware 介面(例如,如果你需要訪問 Environment,則使用 EnvironmentAware)。如果你仍然希望使用建構函式注入其他 bean,則配置屬性 bean 必須用 @Component 註解並使用基於 JavaBean 的屬性繫結。
使用 @ConfigurationProperties 註解的型別
這種配置方式與 SpringApplication 外部 YAML 配置特別搭配,如以下示例所示
my:
service:
remote-address: 192.168.1.1
security:
username: "admin"
roles:
- "USER"
- "ADMIN"
要使用 @ConfigurationProperties bean,你可以像注入任何其他 bean 一樣注入它們,如以下示例所示
-
Java
-
Kotlin
import org.springframework.stereotype.Service;
@Service
public class MyService {
private final MyProperties properties;
public MyService(MyProperties properties) {
this.properties = properties;
}
public void openConnection() {
Server server = new Server(this.properties.getRemoteAddress());
server.start();
// ...
}
// ...
}
import org.springframework.stereotype.Service
@Service
class MyService(val properties: MyProperties) {
fun openConnection() {
val server = Server(properties.remoteAddress)
server.start()
// ...
}
// ...
}
使用 @ConfigurationProperties 還可以生成元資料檔案,IDE 可以使用這些檔案為你的自定義鍵提供自動補全。有關詳細資訊,請參閱附錄。 |
第三方配置
除了使用 @ConfigurationProperties 註解類之外,你還可以在公共 @Bean 方法上使用它。當你想將屬性繫結到不受你控制的第三方元件時,這樣做可能特別有用。
要從 Environment 屬性配置 bean,請將其新增到其 bean 註冊中,如以下示例所示
-
Java
-
Kotlin
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration(proxyBeanMethods = false)
public class ThirdPartyConfiguration {
@Bean
@ConfigurationProperties("another")
public AnotherComponent anotherComponent() {
return new AnotherComponent();
}
}
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
@Configuration(proxyBeanMethods = false)
class ThirdPartyConfiguration {
@Bean
@ConfigurationProperties("another")
fun anotherComponent(): AnotherComponent = AnotherComponent()
}
任何以 another 字首定義的 JavaBean 屬性都將以類似於前面 SomeProperties 示例的方式對映到該 AnotherComponent bean。
寬鬆繫結
Spring Boot 採用一些寬鬆的規則將 Environment 屬性繫結到 @ConfigurationProperties bean,因此 Environment 屬性名和 bean 屬性名之間不需要完全匹配。這在連字元分隔的環境屬性(例如,context-path 繫結到 contextPath)和首字母大寫的環境屬性(例如,PORT 繫結到 port)等常見示例中很有用。
例如,考慮以下 @ConfigurationProperties 類
-
Java
-
Kotlin
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties("my.main-project.person")
public class MyPersonProperties {
private String firstName;
public String getFirstName() {
return this.firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
}
import org.springframework.boot.context.properties.ConfigurationProperties
@ConfigurationProperties("my.main-project.person")
class MyPersonProperties {
var firstName: String? = null
}
使用前面的程式碼,以下屬性名稱都可以使用
| 財產 | 備註 |
|---|---|
|
烤肉串式命名,推薦用於 |
|
標準駝峰式命名語法。 |
|
下劃線表示法,是 |
|
大寫格式,推薦在系統環境變數中使用。 |
註解的 prefix 值必須是 kebab-case(小寫並用 - 分隔,例如 my.main-project.person)。 |
| 屬性源 | 簡單 | List |
|---|---|---|
屬性檔案 |
駝峰式、烤肉串式或下劃線表示法 |
使用 |
YAML 檔案 |
駝峰式、烤肉串式或下劃線表示法 |
標準 YAML 列表語法或逗號分隔值 |
環境變數 |
大寫格式,下劃線作為分隔符(請參閱從環境變數繫結)。 |
用下劃線包圍的數字值(請參閱從環境變數繫結) |
系統屬性 |
駝峰式、烤肉串式或下劃線表示法 |
使用 |
我們建議在可能的情況下,屬性以小寫烤肉串格式儲存,例如 my.person.first-name=Rod。 |
繫結 Map
繫結到 Map 屬性時,你可能需要使用特殊的方括號表示法,以便保留原始 key 值。如果鍵未用 [] 括起來,則任何非字母數字、- 或 . 的字元都將被移除。
例如,考慮將以下屬性繫結到 Map<String,String>
-
屬性
-
YAML
my.map[/key1]=value1
my.map[/key2]=value2
my.map./key3=value3
my:
map:
"[/key1]": "value1"
"[/key2]": "value2"
"/key3": "value3"
| 對於 YAML 檔案,鍵需要用引號括起來才能正確解析。 |
上面的屬性將繫結到一個 Map,其中 /key1、/key2 和 key3 作為 Map 中的鍵。斜槓已從 key3 中移除,因為它沒有用方括號括起來。
當繫結到標量值時,包含 . 的鍵不需要用 [] 括起來。標量值包括列舉和 java.lang 包中除 Object 之外的所有型別。將 a.b=c 繫結到 Map<String, String> 將保留鍵中的 . 並返回一個包含條目 {"a.b"="c"} 的 Map。對於任何其他型別,如果你的 key 包含 .,則需要使用方括號表示法。例如,將 a.b=c 繫結到 Map<String, Object> 將返回一個包含條目 {"a"={"b"="c"}} 的 Map,而 [a.b]=c 將返回一個包含條目 {"a.b"="c"} 的 Map。
從環境變數繫結
大多數作業系統對可用於環境變數的名稱施加了嚴格的規則。例如,Linux shell 變數只能包含字母(a 到 z 或 A 到 Z)、數字(0 到 9)或下劃線字元(_)。按照慣例,Unix shell 變數的名稱也採用大寫形式。
Spring Boot 的寬鬆繫結規則儘可能地旨在與這些命名限制相容。
要將規範形式的屬性名稱轉換為環境變數名稱,你可以遵循以下規則
-
將點號 (
.) 替換為下劃線 (_)。 -
移除所有連字元 (
-)。 -
轉換為大寫。
例如,配置屬性 spring.main.log-startup-info 將是名為 SPRING_MAIN_LOGSTARTUPINFO 的環境變數。
在繫結到物件列表時,也可以使用環境變數。要繫結到 List,元素編號應在變數名稱中用下劃線包圍。
例如,配置屬性 my.service[0].other 將使用名為 MY_SERVICE_0_OTHER 的環境變數。
從環境變數繫結的支援應用於 systemEnvironment 屬性源和任何以 -systemEnvironment 結尾的附加屬性源。
從環境變數繫結 Map
當 Spring Boot 將環境變數繫結到屬性類時,它會在繫結之前將環境變數名稱轉換為小寫。大多數時候,這個細節並不重要,除非繫結到 Map 屬性。
Map 中的鍵始終是小寫,如以下示例所示
-
Java
-
Kotlin
import java.util.HashMap;
import java.util.Map;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties("my.props")
public class MyMapsProperties {
private final Map<String, String> values = new HashMap<>();
public Map<String, String> getValues() {
return this.values;
}
}
import org.springframework.boot.context.properties.ConfigurationProperties
@ConfigurationProperties("my.props")
class MyMapsProperties {
val values: Map<String, String> = HashMap()
}
當設定 MY_PROPS_VALUES_KEY=value 時,values Map 包含一個 {"key"="value"} 條目。
只有環境變數名稱被轉換為小寫,而不是值。當設定 MY_PROPS_VALUES_KEY=VALUE 時,values Map 包含一個 {"key"="VALUE"} 條目。
快取
寬鬆繫結使用快取來提高效能。預設情況下,此快取僅應用於不可變屬性源。要自定義此行為,例如為可變屬性源啟用快取,請使用 ConfigurationPropertyCaching。
合併複雜型別
當列表在多個地方配置時,覆蓋機制會替換整個列表。
例如,假設一個 MyPojo 物件,其 name 和 description 屬性預設為空。以下示例從 MyProperties 暴露了一個 MyPojo 物件列表
-
Java
-
Kotlin
import java.util.ArrayList;
import java.util.List;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties("my")
public class MyProperties {
private final List<MyPojo> list = new ArrayList<>();
public List<MyPojo> getList() {
return this.list;
}
}
import org.springframework.boot.context.properties.ConfigurationProperties
@ConfigurationProperties("my")
class MyProperties {
val list: List<MyPojo> = ArrayList()
}
考慮以下配置
-
屬性
-
YAML
my.list[0].name=my name
my.list[0].description=my description
#---
spring.config.activate.on-profile=dev
my.list[0].name=my another name
my:
list:
- name: "my name"
description: "my description"
---
spring:
config:
activate:
on-profile: "dev"
my:
list:
- name: "my another name"
如果 dev 配置檔案未啟用,則 MyProperties.list 包含一個 MyPojo 條目,如先前定義。但是,如果 dev 配置檔案已啟用,則 list 仍然只包含一個條目(名稱為 my another name,描述為 null)。此配置不會向列表中新增第二個 MyPojo 例項,也不會合並這些項。
當一個 List 在多個配置檔案中指定時,僅使用優先順序最高的那個(且僅使用那個)。考慮以下示例
-
屬性
-
YAML
my.list[0].name=my name
my.list[0].description=my description
my.list[1].name=another name
my.list[1].description=another description
#---
spring.config.activate.on-profile=dev
my.list[0].name=my another name
my:
list:
- name: "my name"
description: "my description"
- name: "another name"
description: "another description"
---
spring:
config:
activate:
on-profile: "dev"
my:
list:
- name: "my another name"
在前面的示例中,如果 dev 配置檔案處於活動狀態,則 MyProperties.list 包含一個 MyPojo 條目(名稱為 my another name,描述為 null)。對於 YAML,逗號分隔列表和 YAML 列表都可以用於完全覆蓋列表內容。
對於 Map 屬性,你可以使用從多個源獲取的屬性值進行繫結。但是,對於多個源中的相同屬性,將使用優先順序最高的屬性。以下示例從 MyProperties 暴露了一個 Map<String, MyPojo>
-
Java
-
Kotlin
import java.util.LinkedHashMap;
import java.util.Map;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties("my")
public class MyProperties {
private final Map<String, MyPojo> map = new LinkedHashMap<>();
public Map<String, MyPojo> getMap() {
return this.map;
}
}
import org.springframework.boot.context.properties.ConfigurationProperties
@ConfigurationProperties("my")
class MyProperties {
val map: Map<String, MyPojo> = LinkedHashMap()
}
考慮以下配置
-
屬性
-
YAML
my.map.key1.name=my name 1
my.map.key1.description=my description 1
#---
spring.config.activate.on-profile=dev
my.map.key1.name=dev name 1
my.map.key2.name=dev name 2
my.map.key2.description=dev description 2
my:
map:
key1:
name: "my name 1"
description: "my description 1"
---
spring:
config:
activate:
on-profile: "dev"
my:
map:
key1:
name: "dev name 1"
key2:
name: "dev name 2"
description: "dev description 2"
如果 dev 配置檔案未啟用,MyProperties.map 包含一個鍵為 key1 的條目(名稱為 my name 1,描述為 my description 1)。但是,如果 dev 配置檔案已啟用,map 將包含兩個條目,鍵分別為 key1(名稱為 dev name 1,描述為 my description 1)和 key2(名稱為 dev name 2,描述為 dev description 2)。
| 前面的合併規則適用於所有屬性源的屬性,而不僅僅是檔案。 |
屬性轉換
當 Spring Boot 繫結到 @ConfigurationProperties bean 時,它會嘗試將外部應用程式屬性強制轉換為正確的型別。如果你需要自定義型別轉換,可以提供一個 ConversionService bean(bean 名稱為 conversionService)或自定義屬性編輯器(透過 CustomEditorConfigurer bean)或自定義轉換器(帶有註解為 @ConfigurationPropertiesBinding 的 bean 定義)。
|
用於屬性轉換的 bean 在應用程式生命週期中很早就會被請求,因此請確保限制你的 |
如果你的自定義 ConversionService 不需要用於配置鍵強制轉換,並且只依賴於用 @ConfigurationPropertiesBinding 限定的自定義轉換器,你可能需要重新命名它。當用 @ConfigurationPropertiesBinding 限定一個 @Bean 方法時,該方法應該是 static 的,以避免“bean 不適合被所有 BeanPostProcessors 處理”的警告。 |
轉換持續時間
Spring Boot 專門支援表示持續時間。如果你暴露一個 Duration 屬性,應用程式屬性中提供以下格式
-
一個常規的
long表示(預設單位是毫秒,除非指定了@DurationUnit) -
一種更易讀的格式,值和單位耦合在一起(
10s表示 10 秒)
考慮以下示例
-
Java
-
Kotlin
import java.time.Duration;
import java.time.temporal.ChronoUnit;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.convert.DurationUnit;
@ConfigurationProperties("my")
public class MyProperties {
@DurationUnit(ChronoUnit.SECONDS)
private Duration sessionTimeout = Duration.ofSeconds(30);
private Duration readTimeout = Duration.ofMillis(1000);
// getters / setters...
public Duration getSessionTimeout() {
return this.sessionTimeout;
}
public void setSessionTimeout(Duration sessionTimeout) {
this.sessionTimeout = sessionTimeout;
}
public Duration getReadTimeout() {
return this.readTimeout;
}
public void setReadTimeout(Duration readTimeout) {
this.readTimeout = readTimeout;
}
}
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.boot.convert.DurationUnit
import java.time.Duration
import java.time.temporal.ChronoUnit
@ConfigurationProperties("my")
class MyProperties {
@DurationUnit(ChronoUnit.SECONDS)
var sessionTimeout = Duration.ofSeconds(30)
var readTimeout = Duration.ofMillis(1000)
}
要指定 30 秒的會話超時,30、PT30S 和 30s 都是等效的。500 毫秒的讀取超時可以透過以下任何一種形式指定:500、PT0.5S 和 500ms。
你也可以使用任何支援的單位。它們是
-
ns表示納秒 -
us表示微秒 -
ms表示毫秒 -
s表示秒 -
m表示分鐘 -
h表示小時 -
d表示天
預設單位是毫秒,可以使用 @DurationUnit 覆蓋,如上面的示例所示。
如果你更喜歡使用建構函式繫結,可以暴露相同的屬性,如以下示例所示
-
Java
-
Kotlin
import java.time.Duration;
import java.time.temporal.ChronoUnit;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.bind.DefaultValue;
import org.springframework.boot.convert.DurationUnit;
@ConfigurationProperties("my")
public class MyProperties {
// fields...
private final Duration sessionTimeout;
private final Duration readTimeout;
public MyProperties(@DurationUnit(ChronoUnit.SECONDS) @DefaultValue("30s") Duration sessionTimeout,
@DefaultValue("1000ms") Duration readTimeout) {
this.sessionTimeout = sessionTimeout;
this.readTimeout = readTimeout;
}
// getters...
public Duration getSessionTimeout() {
return this.sessionTimeout;
}
public Duration getReadTimeout() {
return this.readTimeout;
}
}
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.boot.context.properties.bind.DefaultValue
import org.springframework.boot.convert.DurationUnit
import java.time.Duration
import java.time.temporal.ChronoUnit
@ConfigurationProperties("my")
class MyProperties(@param:DurationUnit(ChronoUnit.SECONDS) @param:DefaultValue("30s") val sessionTimeout: Duration,
@param:DefaultValue("1000ms") val readTimeout: Duration)
如果您正在升級 Long 屬性,請確保定義單位(使用 @DurationUnit),如果它不是毫秒。這樣做可以提供透明的升級路徑,同時支援更豐富的格式。 |
轉換週期
除了持續時間,Spring Boot 還可以處理 Period 型別。在應用程式屬性中可以使用以下格式
-
一個普通的
int表示(使用天作為預設單位,除非指定了@PeriodUnit) -
一種更簡單的格式,其中值和單位對是耦合的(
1y3d表示 1 年零 3 天)
簡單格式支援以下單位
-
y表示年 -
m表示月 -
w表示周 -
d表示天
Period 型別實際上從不儲存週數,它是一個表示“7 天”的快捷方式。 |
轉換資料大小
-
一個普通的
long表示(使用位元組作為預設單位,除非指定了@DataSizeUnit) -
一種更易讀的格式,其中值和單位是耦合的(
10MB表示 10 兆位元組)
考慮以下示例
-
Java
-
Kotlin
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.convert.DataSizeUnit;
import org.springframework.util.unit.DataSize;
import org.springframework.util.unit.DataUnit;
@ConfigurationProperties("my")
public class MyProperties {
@DataSizeUnit(DataUnit.MEGABYTES)
private DataSize bufferSize = DataSize.ofMegabytes(2);
private DataSize sizeThreshold = DataSize.ofBytes(512);
// getters/setters...
public DataSize getBufferSize() {
return this.bufferSize;
}
public void setBufferSize(DataSize bufferSize) {
this.bufferSize = bufferSize;
}
public DataSize getSizeThreshold() {
return this.sizeThreshold;
}
public void setSizeThreshold(DataSize sizeThreshold) {
this.sizeThreshold = sizeThreshold;
}
}
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.boot.convert.DataSizeUnit
import org.springframework.util.unit.DataSize
import org.springframework.util.unit.DataUnit
@ConfigurationProperties("my")
class MyProperties {
@DataSizeUnit(DataUnit.MEGABYTES)
var bufferSize = DataSize.ofMegabytes(2)
var sizeThreshold = DataSize.ofBytes(512)
}
要指定 10 兆位元組的緩衝區大小,10 和 10MB 是等效的。256 位元組的大小閾值可以指定為 256 或 256B。
你也可以使用任何支援的單位。它們是
-
B表示位元組 -
KB表示千位元組 -
MB表示兆位元組 -
GB表示千兆位元組 -
TB表示萬億位元組
預設單位是位元組,可以使用 @DataSizeUnit 覆蓋,如上面的示例所示。
如果你更喜歡使用建構函式繫結,可以暴露相同的屬性,如以下示例所示
-
Java
-
Kotlin
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.bind.DefaultValue;
import org.springframework.boot.convert.DataSizeUnit;
import org.springframework.util.unit.DataSize;
import org.springframework.util.unit.DataUnit;
@ConfigurationProperties("my")
public class MyProperties {
// fields...
private final DataSize bufferSize;
private final DataSize sizeThreshold;
public MyProperties(@DataSizeUnit(DataUnit.MEGABYTES) @DefaultValue("2MB") DataSize bufferSize,
@DefaultValue("512B") DataSize sizeThreshold) {
this.bufferSize = bufferSize;
this.sizeThreshold = sizeThreshold;
}
// getters...
public DataSize getBufferSize() {
return this.bufferSize;
}
public DataSize getSizeThreshold() {
return this.sizeThreshold;
}
}
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.boot.context.properties.bind.DefaultValue
import org.springframework.boot.convert.DataSizeUnit
import org.springframework.util.unit.DataSize
import org.springframework.util.unit.DataUnit
@ConfigurationProperties("my")
class MyProperties(@param:DataSizeUnit(DataUnit.MEGABYTES) @param:DefaultValue("2MB") val bufferSize: DataSize,
@param:DefaultValue("512B") val sizeThreshold: DataSize)
如果您正在升級 Long 屬性,請確保定義單位(使用 @DataSizeUnit),如果它不是位元組。這樣做可以提供透明的升級路徑,同時支援更豐富的格式。 |
@ConfigurationProperties 驗證
只要 @ConfigurationProperties 類使用 Spring 的 @Validated 註解進行註解,Spring Boot 就會嘗試驗證它們。您可以在配置類上直接使用 JSR-303 jakarta.validation 約束註解。為此,請確保您的類路徑中包含相容的 JSR-303 實現,然後將約束註解新增到您的欄位中,如以下示例所示
-
Java
-
Kotlin
import java.net.InetAddress;
import jakarta.validation.constraints.NotNull;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.validation.annotation.Validated;
@ConfigurationProperties("my.service")
@Validated
public class MyProperties {
@NotNull
private InetAddress remoteAddress;
// getters/setters...
public InetAddress getRemoteAddress() {
return this.remoteAddress;
}
public void setRemoteAddress(InetAddress remoteAddress) {
this.remoteAddress = remoteAddress;
}
}
import jakarta.validation.constraints.NotNull
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.validation.annotation.Validated
import java.net.InetAddress
@ConfigurationProperties("my.service")
@Validated
class MyProperties {
var remoteAddress: @NotNull InetAddress? = null
}
您還可以透過使用 @Validated 註解建立配置屬性的 @Bean 方法來觸發驗證。 |
為了將驗證級聯到巢狀屬性,關聯欄位必須使用 @Valid 進行註解。以下示例基於前面的 MyProperties 示例
-
Java
-
Kotlin
import java.net.InetAddress;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.validation.annotation.Validated;
@ConfigurationProperties("my.service")
@Validated
public class MyProperties {
@NotNull
private InetAddress remoteAddress;
@Valid
private final Security security = new Security();
// getters/setters...
public InetAddress getRemoteAddress() {
return this.remoteAddress;
}
public void setRemoteAddress(InetAddress remoteAddress) {
this.remoteAddress = remoteAddress;
}
public Security getSecurity() {
return this.security;
}
public static class Security {
@NotEmpty
private String username;
// getters/setters...
public String getUsername() {
return this.username;
}
public void setUsername(String username) {
this.username = username;
}
}
}
import jakarta.validation.Valid
import jakarta.validation.constraints.NotEmpty
import jakarta.validation.constraints.NotNull
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.validation.annotation.Validated
import java.net.InetAddress
@ConfigurationProperties("my.service")
@Validated
class MyProperties {
var remoteAddress: @NotNull InetAddress? = null
@Valid
val security = Security()
class Security {
@NotEmpty
var username: String? = null
}
}
您還可以透過建立名為 configurationPropertiesValidator 的 bean 定義來新增自定義 Spring Validator。@Bean 方法應該宣告為 static。配置屬性驗證器在應用程式生命週期的早期建立,將 @Bean 方法宣告為靜態允許在不例項化 @Configuration 類的情況下建立 bean。這樣做可以避免早期例項化可能引起的任何問題。
spring-boot-actuator 模組包含一個暴露所有 @ConfigurationProperties bean 的端點。將您的網路瀏覽器指向 /actuator/configprops 或使用等效的 JMX 端點。有關詳細資訊,請參閱生產就緒功能部分。 |
@ConfigurationProperties 與 @Value
@Value 註解是核心容器功能,它不提供與型別安全配置屬性相同的功能。下表總結了 @ConfigurationProperties 和 @Value 支援的功能
| 功能 | @ConfigurationProperties |
@Value |
|---|---|---|
是 |
有限(參見下面的註釋) |
|
是 |
否 |
|
|
否 |
是 |
|
如果您確實想使用 例如, |
如果您為自己的元件定義了一組配置鍵,我們建議您將它們分組到一個使用 @ConfigurationProperties 註解的 POJO 中。這樣做將為您提供結構化、型別安全的物件,您可以將其注入到自己的 bean 中。