外部組態
Spring Boot 讓您可以外部化您的組態,以便您可以在不同的環境中使用相同的應用程式程式碼。您可以使用各種外部組態來源,包括 Java 屬性檔案、YAML 檔案、環境變數和命令列引數。
屬性值可以使用 @Value
註解直接注入到您的 beans 中,透過 Spring 的 Environment
抽象存取,或者透過 @ConfigurationProperties
繫結到結構化物件。
Spring Boot 使用非常特定的 PropertySource
順序,旨在允許合理地覆寫值。後面的屬性來源可以覆寫先前來源中定義的值。來源會依以下順序考量:
-
預設屬性(透過設定
SpringApplication.setDefaultProperties
指定)。 -
您
@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 啟用時,Devtools 全域設定屬性 在
$HOME/.config/spring-boot
目錄中。
組態資料檔案會依以下順序考量:
-
封裝在您的 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/
子目錄的直接子目錄
-
清單依優先順序排序(較低項目的值覆寫較早項目的值)。從載入的檔案中取得的文件會作為 PropertySources
新增到 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 很早就會使用,以判斷必須載入哪些檔案。它們必須定義為環境屬性(通常是作業系統環境變數、系統屬性或命令列引數)。 |
如果 spring.config.location
包含目錄(而不是檔案),它們應該以 /
結尾。在執行階段,它們將附加從 spring.config.name
產生的名稱,然後再載入。在 spring.config.location
中指定的檔案會直接匯入。
目錄和檔案位置值也會展開以檢查特定設定檔檔案。例如,如果您具有 classpath:myconfig.properties 的 spring.config.location ,您也會找到適當的 classpath:myconfig-<profile>.properties 檔案被載入。 |
在大多數情況下,您新增的每個 spring.config.location
項目都將參考單個檔案或目錄。位置會依定義的順序處理,後面的位置可以覆寫較早位置的值。
如果您有複雜的位置設定,並且您使用特定設定檔組態檔案,則您可能需要提供進一步的提示,以便 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
宣告一起使用。
例如,spring.config.import
值為 optional:file:./myconfig.properties
允許您的應用程式啟動,即使 myconfig.properties
檔案遺失。
如果您想要忽略所有 ConfigDataLocationNotFoundExceptions
並始終繼續啟動您的應用程式,您可以使用 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
相同的位置載入,特定設定檔檔案始終覆寫非特定檔案。如果指定了多個設定檔,則適用後勝策略。例如,如果設定檔 prod,live
由 spring.profiles.active
屬性指定,則 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
檔案中可能具有以下內容:
-
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
重新定義為不同的值。
無論宣告多少次,匯入都只會匯入一次。匯入在 properties/yaml 檔案的單個文件中定義的順序無關緊要。例如,以下兩個範例產生相同的結果:
-
Properties
-
YAML
spring.config.import=my.properties
my.property=value
spring:
config:
import: "my.properties"
my:
property: "value"
-
Properties
-
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
匯入它:
-
Properties
-
YAML
spring.config.import=file:/etc/config/myconfig[.yaml]
spring:
config:
import: "file:/etc/config/myconfig[.yaml]"
使用組態樹
在雲端平台(例如 Kubernetes)上執行應用程式時,您經常需要讀取平台提供的組態值。為此目的使用環境變數並不少見,但這可能存在缺點,尤其是在應該保密值的情況下。
除了環境變數之外,許多雲端平台現在允許您將組態對應到掛載的資料卷中。例如,Kubernetes 可以將 ConfigMaps
和 Secrets
卷掛載。
有兩種常見的卷掛載模式可以使用
-
單一檔案包含完整的屬性集(通常寫為 YAML)。
-
多個檔案寫入到目錄樹中,檔名成為「鍵 (key)」,內容成為「值 (value)」。
對於第一種情況,您可以直接使用 spring.config.import
匯入 YAML 或 Properties 檔案,如上方所述。對於第二種情況,您需要使用 configtree:
前綴,以便 Spring Boot 知道它需要將所有檔案公開為屬性。
舉例來說,假設 Kubernetes 已經掛載了以下卷
etc/
config/
myapp/
username
password
username
檔案的內容將會是組態值,而 password
的內容將會是密碼。
若要匯入這些屬性,您可以將以下內容新增到您的 application.properties
或 application.yaml
檔案中
-
Properties
-
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/*/
作為匯入位置
-
Properties
-
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 環境
-
Properties
-
YAML
spring.config.import=optional:configtree:/run/secrets/
spring:
config:
import: "optional:configtree:/run/secrets/"
屬性預留位置 (Property Placeholders)
application.properties
和 application.yaml
中的值在使用時會透過現有的 Environment
進行篩選,因此您可以回溯參考先前定義的值(例如,來自系統屬性或環境變數)。標準的 ${name}
屬性預留位置語法可以在值內的任何位置使用。屬性預留位置也可以使用 :
來指定預設值,以分隔預設值和屬性名稱,例如 ${name:default}
。
以下範例顯示了使用和不使用預設值的預留位置
-
Properties
-
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
。
您應該始終在預留位置中使用屬性名稱的標準形式(僅使用小寫字母的 kebab-case)。這將允許 Spring Boot 使用與寬鬆繫結 例如, |
您也可以使用此技術來建立現有 Spring Boot 屬性的「簡短」變體。請參閱「操作指南」中的使用「簡短」命令列引數章節以取得詳細資訊。 |
使用多文件檔案 (Working With Multi-Document Files)
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 註解載入。 |
啟用屬性 (Activation Properties)
有時,僅在滿足特定條件時才啟用給定的屬性集會很有用。例如,您可能有一些屬性僅在特定 Profile 啟用時才相關。
您可以使用 spring.config.activate.*
有條件地啟用屬性文件。
以下啟用屬性可用
屬性 (Property) | 注意 (Note) |
---|---|
|
文件必須符合的 Profile 表達式才能啟用。 |
|
偵測到文件必須符合的 |
例如,以下指定第二個文件僅在 Kubernetes 上執行時啟用,並且僅在啟用 “prod” 或 “staging” Profile 時啟用
-
Properties
-
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"
加密屬性 (Encrypting Properties)
Spring Boot 未提供任何內建的屬性值加密支援,但是,它確實提供了修改 Spring Environment
中包含的值所需的掛鉤點。EnvironmentPostProcessor
介面允許您在應用程式啟動之前操作 Environment
。請參閱在應用程式啟動之前自訂 Environment 或 ApplicationContext 以取得詳細資訊。
如果您需要安全的方式來儲存憑證和密碼,Spring Cloud Vault 專案提供在 HashiCorp Vault 中儲存外部化組態的支援。
使用 YAML (Working With YAML)
YAML 是 JSON 的超集合,因此,它是指定階層式組態資料的便捷格式。只要您的類別路徑上有 SnakeYAML 程式庫,SpringApplication
類別就會自動支援 YAML 作為屬性的替代方案。
如果您使用 starters,spring-boot-starter 會自動提供 SnakeYAML。 |
將 YAML 對應到屬性 (Mapping YAML to Properties)
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
使用 [index] 表示法的屬性可以使用 Spring Boot 的 Binder 類別繫結到 Java List 或 Set 物件。如需更多詳細資訊,請參閱下方的類型安全組態屬性章節。 |
YAML 檔案無法使用 @PropertySource 或 @TestPropertySource 註解載入。因此,如果您需要以這種方式載入值,則需要使用屬性檔案。 |
設定隨機值 (Configuring Random Values)
RandomValuePropertySource
對於注入隨機值(例如,注入到密碼或測試案例中)很有用。它可以產生整數、長整數、uuid 或字串,如下列範例所示
-
Properties
-
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
是最大值(不包含)。
設定系統環境屬性 (Configuring System Environment Properties)
Spring Boot 支援為環境屬性設定前綴。如果系統環境由多個具有不同組態需求的 Spring Boot 應用程式共用,這會很有用。系統環境屬性的前綴可以直接在 SpringApplication
上設定。
例如,如果您將前綴設定為 input
,則系統環境中諸如 remote.timeout
之類的屬性也會解析為 input.remote.timeout
。
類型安全組態屬性 (Type-safe Configuration Properties)
使用 @Value("${property}")
註解來注入組態屬性有時可能很麻煩,尤其是當您處理多個屬性或您的資料本質上是階層式時。Spring Boot 提供了一種替代方法來處理屬性,該方法允許強型別 Bean 管理和驗證應用程式的組態。
另請參閱 @Value 和類型安全組態屬性之間的差異。 |
JavaBean 屬性繫結 (JavaBean Properties Binding)
可以繫結宣告標準 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
,具有預設為USER
的String
集合。
對應到 Spring Boot 中可用的 @ConfigurationProperties 類別的屬性,這些屬性是透過屬性檔案、YAML 檔案、環境變數和其他機制設定的,是公開 API,但類別本身的存取器(getter/setter)並非旨在直接使用。 |
這種安排依賴於預設的空建構子,並且通常強制要求 getter 和 setter,因為繫結是透過標準 Java Beans 屬性描述器進行的,就像在 Spring MVC 中一樣。在以下情況下,可以省略 setter
有些人使用 Project Lombok 自動新增 getter 和 setter。請確保 Lombok 沒有為此類類型產生任何特定的建構子,因為容器會自動使用它來實例化物件。 最後,僅考慮標準 Java Bean 屬性,並且不支援靜態屬性的繫結。 |
建構子繫結 (Constructor Binding)
前一節中的範例可以用不可變的方式重寫,如下列範例所示
-
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>)
}
在此設定中,單一參數化建構子的存在意味著應使用建構子繫結。這表示 binder 將會找到一個建構子,其中包含您希望繫結的參數。如果您的類別有多個建構子,則可以使用 @ConstructorBinding
註解來指定要用於建構子繫結的建構子。若要選擇不使用具有單一參數化建構子的類別的建構子繫結,則建構子必須使用 @Autowired
註解或設為 private
。建構子繫結可以與 records 一起使用。除非您的 record 有多個建構子,否則無需使用 @ConstructorBinding
。
建構子繫結類別的巢狀成員(例如上述範例中的 Security
)也將透過其建構子繫結。
可以使用建構子參數和 record 元件上的 @DefaultValue
來指定預設值。轉換服務將用於強制轉換註解的 String
值為遺失屬性的目標類型。
參考先前的範例,如果沒有屬性繫結到 Security
,則 MyProperties
實例將包含 security
的 null
值。為了使其即使在沒有屬性繫結到它時也包含 Security
的非 null 實例(當使用 Kotlin 時,這將需要將 Security
的 username
和 password
參數宣告為可為 null,因為它們沒有預設值),請使用空的 @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 ,則會自動發生這種情況。 |
不建議將 java.util.Optional 與 @ConfigurationProperties 一起使用,因為它主要用於作為傳回類型。因此,它不太適合組態屬性注入。為了與其他類型的屬性保持一致,如果您確實宣告了 Optional 屬性且它沒有值,則會繫結 null 而不是空的 Optional 。 |
若要在屬性名稱中使用保留關鍵字,例如 my.service.import ,請在建構子參數上使用 @Name 註解。 |
啟用 @ConfigurationProperties 註解類型 (Enabling @ConfigurationProperties-annotated Types)
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
僅處理環境,特別是不從 Context 注入其他 Bean。對於特殊情況,可以使用 setter 注入或框架提供的任何 *Aware
介面(例如,如果您需要存取 Environment
,則可以使用 EnvironmentAware
)。如果您仍然想使用建構子注入其他 Bean,則組態屬性 Bean 必須使用 @Component
註解並使用基於 JavaBean 的屬性繫結。
使用 @ConfigurationProperties 註解類型 (Using @ConfigurationProperties-annotated Types)
這種組態樣式特別適用於 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 可以使用這些檔案為您自己的鍵提供自動完成功能。請參閱附錄以取得詳細資訊。 |
第三方組態 (Third-party Configuration)
除了使用 @ConfigurationProperties
註解類別之外,您也可以在公開的 @Bean
方法上使用它。當您想要將屬性繫結到不受您控制的第三方組件時,這樣做可能特別有用。
若要從 Environment
屬性組態 Bean,請將 @ConfigurationProperties
新增到其 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(prefix = "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(prefix = "another")
fun anotherComponent(): AnotherComponent = AnotherComponent()
}
任何使用 another
前綴定義的 JavaBean 屬性都會以與先前 SomeProperties
範例類似的方式對應到該 AnotherComponent
Bean。
寬鬆繫結 (Relaxed Binding)
Spring Boot 對於將 Environment
屬性繫結到 @ConfigurationProperties
Bean 使用了一些寬鬆的規則,因此 Environment
屬性名稱和 Bean 屬性名稱之間不需要完全匹配。這在常見的範例中很有用,包括以破折號分隔的環境屬性(例如,context-path
繫結到 contextPath
)和首字母大寫的環境屬性(例如,PORT
繫結到 port
)。
舉例來說,考慮以下 @ConfigurationProperties
類別
-
Java
-
Kotlin
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "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(prefix = "my.main-project.person")
class MyPersonProperties {
var firstName: String? = null
}
使用先前的程式碼,可以使用以下所有屬性名稱
屬性 (Property) | 注意 (Note) |
---|---|
|
Kebab case,建議用於 |
|
標準 camel case 語法。 |
|
底線表示法,這是用於 |
|
大寫格式,建議在使用系統環境變數時使用。 |
註解的 prefix 值必須採用 kebab case 形式(小寫並以 - 分隔,例如 my.main-project.person )。 |
屬性來源 (Property Source) | 簡單 (Simple) | 列表 (List) |
---|---|---|
屬性檔案 (Properties Files) |
Camel case、kebab case 或底線表示法 |
使用 |
YAML 檔案 (YAML Files) |
Camel case、kebab case 或底線表示法 |
標準 YAML 列表語法或逗號分隔值 |
環境變數 (Environment Variables) |
以底線作為分隔符號的大寫格式(請參閱從環境變數繫結)。 |
以底線包圍的數值(請參閱從環境變數繫結) |
系統屬性 (System properties) |
Camel case、kebab case 或底線表示法 |
使用 |
我們建議盡可能將屬性儲存在小寫 kebab 格式中,例如 my.person.first-name=Rod 。 |
繫結 Maps (Binding Maps)
當繫結到 Map
屬性時,您可能需要使用特殊的方括號表示法,以便保留原始的 key
值。如果鍵未以 []
包圍,則會移除任何非英數字元、-
或 .
的字元。
例如,考慮將以下屬性繫結到 Map<String,String>
-
Properties
-
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。
從環境變數繫結 (Binding From Environment Variables)
大多數作業系統對可用於環境變數的名稱施加嚴格的規則。例如,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
結尾的其他屬性來源。
從環境變數繫結 Maps (Binding Maps From Environment Variables)
當 Spring Boot 將環境變數繫結到屬性類別時,它會在繫結之前將環境變數名稱轉換為小寫。大多數時候,此細節並不重要,除非繫結到 Map
屬性。
Map
中的鍵始終為小寫,如下列範例所示
-
Java
-
Kotlin
import java.util.HashMap;
import java.util.Map;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "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(prefix = "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"}
條目。
合併複雜類型 (Merging Complex Types)
當在多個位置設定列表時,覆寫會透過替換整個列表來運作。
例如,假設一個 MyPojo
物件,其 name
和 description
屬性依預設為 null
。以下範例從 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()
}
考慮以下組態
-
Properties
-
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
Profile 未啟用,則 MyProperties.list
包含一個 MyPojo
條目,如先前定義的。但是,如果啟用了 dev
Profile,則 list
仍然僅包含一個條目(名稱為 my another name
,描述為 null
)。此組態不會將第二個 MyPojo
實例新增到列表中,也不會合併項目。
當 List
在多個 Profile 中指定時,將使用優先順序最高的 Profile(且僅使用該 Profile)。考慮以下範例
-
Properties
-
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
Profile 已啟用,則 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()
}
考慮以下組態
-
Properties
-
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
Profile 未啟用,則 MyProperties.map
包含一個鍵為 key1
的條目(名稱為 my name 1
,描述為 my description 1
)。但是,如果啟用了 dev
Profile,則 map
包含兩個條目,鍵分別為 key1
(名稱為 dev name 1
,描述為 my description 1
)和 key2
(名稱為 dev name 2
,描述為 dev description 2
)。
先前的合併規則適用於來自所有屬性來源的屬性,而不僅僅是檔案。 |
屬性轉換 (Properties Conversion)
當 Spring Boot 繫結到 @ConfigurationProperties
Bean 時,它會嘗試將外部應用程式屬性強制轉換為正確的類型。如果您需要自訂類型轉換,您可以提供 ConversionService
Bean(Bean 名稱為 conversionService
)或自訂屬性編輯器(透過 CustomEditorConfigurer
Bean)或自訂 Converters
(Bean 定義使用 @ConfigurationPropertiesBinding
註解)。
由於此 Bean 在應用程式生命週期的早期就被請求,因此請確保限制您的 ConversionService 正在使用的依賴項。通常,您所需的任何依賴項可能在建立時尚未完全初始化。如果您的自訂 ConversionService 不是組態鍵強制轉換所必需的,並且僅依賴於使用 @ConfigurationPropertiesBinding 限定詞的自訂轉換器,您可能需要重新命名您的自訂 ConversionService 。 |
轉換持續時間 (Converting Durations)
Spring Boot 專門支援表示持續時間。如果您公開 java.time.Duration
屬性,則應用程式屬性中可以使用以下格式
-
常規
long
表示法(使用毫秒作為預設單位,除非已指定@DurationUnit
) -
java.time.Duration
使用的標準 ISO-8601 格式 -
更易讀的格式,其中值和單位耦合在一起(
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
代表奈秒 (nanoseconds) -
us
代表微秒 (microseconds) -
ms
代表毫秒 (milliseconds) -
s
代表秒 (seconds) -
m
代表分鐘 (minutes) -
h
代表小時 (hours) -
d
代表天 (days)
預設單位為毫秒,可以使用 @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 ),如果它不是毫秒。這樣做可以提供透明的升級路徑,同時支援更豐富的格式。 |
轉換期間 (Converting Periods)
除了持續時間之外,Spring Boot 也可以使用 java.time.Period
類型。以下格式可用於應用程式屬性中
-
常規
int
表示法(使用天作為預設單位,除非已指定@PeriodUnit
) -
java.time.Period
使用的標準 ISO-8601 格式 -
更簡單的格式,其中值和單位對耦合在一起(
1y3d
表示 1 年和 3 天)
簡單格式支援以下單位
-
y
代表年 (years) -
m
代表月份 -
w
代表週 -
d
代表天 (days)
java.time.Period 類型實際上從未儲存週數,它只是一個代表「7 天」的捷徑。 |
轉換資料大小
Spring Framework 具有 DataSize
值類型,用於表示以位元組為單位的大小。如果您公開 DataSize
屬性,則應用程式屬性中可以使用以下格式
-
常規
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
方法宣告為 static 可以讓 Bean 在無需實例化 @Configuration
類別的情況下建立。這樣做可以避免任何可能由早期實例化引起的問題。
spring-boot-actuator 模組包含一個端點,用於公開所有 @ConfigurationProperties Bean。將您的網頁瀏覽器指向 /actuator/configprops 或使用等效的 JMX 端點。有關詳細資訊,請參閱「生產就緒功能」章節。 |
@ConfigurationProperties 與 @Value
@Value
註解是核心容器功能,它不提供與型別安全配置屬性相同的功能。下表總結了 @ConfigurationProperties
和 @Value
支援的功能
功能 | @ConfigurationProperties |
@Value |
---|---|---|
是 |
有限 (請參閱下方註解) |
|
是 |
否 |
|
|
否 |
是 |
如果您確實想使用 例如, |
如果您為自己的元件定義一組配置金鑰,我們建議您將它們分組在以 @ConfigurationProperties
註釋的 POJO 中。這樣做將為您提供結構化、型別安全的物件,您可以將其注入到您自己的 Bean 中。
在剖析這些檔案並填入環境時,不會處理來自應用程式屬性檔案的 SpEL
運算式。但是,可以在 @Value
中編寫 SpEL
運算式。如果應用程式屬性檔案中屬性的值是 SpEL
運算式,則在透過 @Value
使用時將會評估它。