關鍵字:@ConfigurationProperties(prefix="")、@EnableConfigurationProperties(TestClass.class)、@Value、@PropertySource(value={"classpath:test.properties"})
前言
在編寫項目代碼時,我們要求更靈活的配置,更好的模塊化整合。在 Spring Boot 項目中,爲滿足以上要求,我們將大量的參數配置在 application.properties 或 application.yml 文件中,通過 @ConfigurationProperties
註解,我們可以方便的獲取這些參數值
使用 @ConfigurationProperties 配置模塊
假設我們正在搭建一個發送郵件的模塊。在本地測試,我們不想該模塊真的發送郵件,所以我們需要一個參數來「開關」 disable 這個功能。另外,我們希望爲這些郵件配置一個默認的主題,這樣,當我們查看郵件收件箱,通過郵件主題可以快速判斷出這是測試郵件
在 application.properties 文件中創建這些參數:
轉存失敗重新上傳取消轉存失敗重新上傳取消
我們可以使用 @Value
註解或着使用 Spring Environment
bean 訪問這些屬性,是這種注入配置方式有時顯得很笨重。我們將使用更安全的方式(@ConfigurationProperties
)來獲取這些屬性
@ConfigurationProperties
的基本用法非常簡單:我們爲每個要捕獲的外部屬性提供一個帶有字段的類。請注意以下幾點:
- 前綴定義了哪些外部屬性將綁定到類的字段上
- 根據 Spring Boot 寬鬆的綁定規則,類的屬性名稱必須與外部屬性的名稱匹配
- 我們可以簡單地用一個值初始化一個字段來定義一個默認值
- 類本身可以是包私有的
- 類的字段必須有公共 setter 方法
Spring 寬鬆綁定規則 (relaxed binding)
Spring使用一些寬鬆的綁定屬性規則。因此,以下變體都將綁定到 hostName 屬性上:
如果我們將 MailModuleProperties 類型的 bean 注入到另一個 bean 中,這個 bean 現在可以以類型安全的方式訪問那些外部配置參數的值。
但是,我們仍然需要讓 Spring 知道我們的 @ConfigurationProperties 類存在,以便將其加載到應用程序上下文中。
激活 @ConfigurationProperties
對於 Spring Boot,創建一個 MailModuleProperties 類型的 bean,我們可以通過下面幾種方式將其添加到應用上下文中
首先,我們可以通過添加 @Component 註解讓 Component Scan 掃描到
轉存失敗重新上傳取消轉存失敗重新上傳取消
很顯然,只有當類所在的包被 Spring @ComponentScan
註解掃描到纔會生效,默認情況下,該註解會掃描在主應用類下的所有包結構
我們也可以通過 Spring 的 Java Configuration 特性實現同樣的效果:
轉存失敗重新上傳取消轉存失敗重新上傳取消
只要 MailModuleConfiguration 類被 Spring Boot 應用掃描到,我們就可以在應用上下文中訪問 MailModuleProperties bean
我們還可以使用 @EnableConfigurationProperties
註解讓我們的類被 Spring Boot 所知道,在該註解中其實是用了@Import(EnableConfigurationPropertiesImportSelector.class)
實現,大家可以看一下
轉存失敗重新上傳取消轉存失敗重新上傳取消
激活一個 @ConfigurationProperties 類的最佳方式是什麼?
所有上述方法都同樣有效。然而,我建議模塊化你的應用程序,並讓每個模塊提供自己的
@ConfigurationProperties
類,只提供它需要的屬性,就像我們在上面的代碼中對郵件模塊所做的那樣。這使得在不影響其他模塊的情況下重構一個模塊中的屬性變得容易。因此,我不建議在應用程序類本身上使用
@EnableConfigurationProperties
,如許多其他教程中所示,是在特定於模塊的 @Configuration 類上使用@EnableConfigurationProperties
,該類也可以利用包私有的可見性對應用程序的其餘部分隱藏屬性。
無法轉換的屬性
如果我們在 application.properties 屬性上定義的屬性不能被正確的解析會發生什麼?假如我們爲原本應該爲布爾值的屬性提供的值爲 'foo':
轉存失敗重新上傳取消轉存失敗重新上傳取消
默認情況下,Spring Boot 將會啓動失敗,並拋出異常:
Failed to bind properties under 'myapp.mail.enabled' to java.lang.Boolean:
Property: myapp.mail.enabled
Value: foo
Origin: class path resource [application.properties]:1:20
Reason: failed to convert java.lang.String to java.lang.Boolean
當我們爲屬性配置錯誤的值時,而又不希望 Spring Boot 應用啓動失敗,我們可以設置 ignoreInvalidFields
屬性爲 true (默認爲 false)
轉存失敗重新上傳取消轉存失敗重新上傳取消
這樣,Spring Boot 將會設置 enabled 字段爲我們在 Java 代碼裏設定好的默認值。如果我們沒有設置默認值,enabled 將爲 null,因爲這裏定義的是 boolean 的包裝類 Boolean
未知的屬性
和上面的情況有些相反,如果我們在 application.properties 文件提供了 MailModuleProperties 類不知道的屬性會發生什麼?
轉存失敗重新上傳取消轉存失敗重新上傳取消
默認情況下,Spring Boot 會忽略那些不能綁定到 @ConfigurationProperties
類字段的屬性
然而,當配置文件中有一個屬性實際上沒有綁定到 @ConfigurationProperties
類時,我們可能希望啓動失敗。也許我們以前使用過這個配置屬性,但是它已經被刪除了,這種情況我們希望被觸發告知手動從 application.properties 刪除這個屬性
爲了實現上述情況,我們僅需要將 ignoreUnknownFields
屬性設置爲 false (默認是 true)
轉存失敗重新上傳取消轉存失敗重新上傳取消
現在,應用啓動時,控制檯會反饋我們異常信息
Binding to target [Bindable@cf65451 type = com.example.configurationproperties.properties.MailModuleProperties, value = 'provided', annotations = array<Annotation>[@org.springframework.boot.context.properties.ConfigurationProperties(value=myapp.mail, prefix=myapp.mail, ignoreInvalidFields=false, ignoreUnknownFields=false)]] failed:
Property: myapp.mail.unknown-property
Value: foo
Origin: class path resource [application.properties]:3:29
Reason: The elements [myapp.mail.unknown-property] were left unbound.
棄用警告⚠️(Deprecation Warning)
ignoreUnknownFields
在未來 Spring Boot 的版本中會被標記爲 deprecated,因爲我們可能有兩個帶有@ConfigurationProperties
的類,同時綁定到了同一個命名空間 (namespace) 上,其中一個類可能知道某個屬性,另一個類卻不知道某個屬性,這樣就會導致啓動失敗
啓動時校驗 @ConfigurationProperties
如果我們希望配置參數在傳入到應用中時有效的,我們可以通過在字段上添加 bean validation
註解,同時在類上添加 @Validated
註解
轉存失敗重新上傳取消轉存失敗重新上傳取消
如果我們忘記在 application.properties 文件設置 enabled 屬性,並且設置 defaultSubject 爲空
轉存失敗重新上傳取消轉存失敗重新上傳取消
應用啓動時,我們將會得到 BindValidationException
Binding to target org.springframework.boot.context.properties.bind.BindException: Failed to bind properties under 'myapp.mail' to com.example.configurationproperties.properties.MailModuleProperties failed:
Property: myapp.mail.enabled
Value: null
Reason: must not be null
Property: myapp.mail.defaultSubject
Value: null
Reason: must not be empty
當然這些默認的驗證註解不能滿足你的驗證要求,我們也可以自定義註解
如果你的驗證邏輯很特殊,我們可以實現一個方法,並用 @PostConstruct 標記,如果驗證失敗,方法拋出異常即可。
複雜屬性類型
多數情況,我們傳遞給應用的參數是基本的字符串或數字。但是,有時我們需要傳遞諸如 List 的數據類型
List 和 Set
假如,我們爲郵件模塊提供了一個 SMTP 服務的列表,我們可以添加該屬性到 MailModuleProperties 類中
轉存失敗重新上傳取消轉存失敗重新上傳取消
我們有兩種方式讓 Spring Boot 自動填充該 list 屬性
application.properties
在 application.properties 文件中以數組形式書寫
轉存失敗重新上傳取消轉存失敗重新上傳取消
application.yml
YAML 本身支持 list 類型,所以可以在 application.yml 文件中添加:
轉存失敗重新上傳取消轉存失敗重新上傳取消
set 集合也是這種方式的配置方式,不再重複書寫。另外YAML 是更好的閱讀方式,層次分明,所以在實際應用中更推薦大家使用該種方式做數據配置
Duration
Spring Boot 內置支持從配置參數中解析 durations (持續時間),官網文檔 給出了明確的說明
轉存失敗重新上傳取消轉存失敗重新上傳取消
我們既可以配置毫秒數數值,也可配置帶有單位的文本:
轉存失敗重新上傳取消轉存失敗重新上傳取消
官網上已明確說明,配置 duration 不寫單位,默認按照毫秒來指定,我們也可已通過 @DurationUnit 來指定單位:
轉存失敗重新上傳取消轉存失敗重新上傳取消
常用單位如下:
ns
for nanoseconds (納秒)us
for microseconds (微秒)ms
for milliseconds (毫秒)s
for seconds (秒)m
for minutes (分)h
for hours (時)d
for days (天)
DataSize
與 Duration 的用法一毛一樣,默認單位是 byte (字節),可以通過 @DataSizeUnit 單位指定:
轉存失敗重新上傳取消轉存失敗重新上傳取消
但是,我測試的時候打印出來結果都是以 B (bytes) 來顯示
常見單位如下:
B
for bytesKB
for kilobytesMB
for megabytesGB
for gigabytesTB
for terabytes
自定義類型
有些情況,我們想解析配置參數到我們自定義的對象類型上,假設,我們我們設置最大包裹重量:
轉存失敗重新上傳取消轉存失敗重新上傳取消
在 MailModuleProperties 中添加 Weight 屬性
轉存失敗重新上傳取消轉存失敗重新上傳取消
我們可以模仿 DataSize 和 Duration 創造自己的 converter (轉換器)
轉存失敗重新上傳取消轉存失敗重新上傳取消
將其註冊到 Spring Boot 上下文中
轉存失敗重新上傳取消轉存失敗重新上傳取消
@ConfigurationPropertiesBinding
註解是讓 Spring Boot 知道使用該轉換器做數據綁定
使用 Spring Boot Configuration Processor 完成自動補全
我們向項目中添加依賴:
Maven
Gradle
重新 build 項目之後,configuration processor 會爲我們創建一個 JSON 文件:
轉存失敗重新上傳取消轉存失敗重新上傳取消
這樣,當我們在 application.properties 和 application.yml 中寫配置的時候會有自動提醒:
轉存失敗重新上傳取消轉存失敗重新上傳取消
標記配置屬性爲 Deprecated
configuration processor 允許我們標記某一個屬性爲 deprecated
轉存失敗重新上傳取消轉存失敗重新上傳取消
我們可以通過添加 @DeprecatedConfigurationProperty
註解到字段的 getter 方法上,來標示該字段爲 deprecated,重新 build 項目,看看 JSON 文件發生了什麼?
轉存失敗重新上傳取消轉存失敗重新上傳取消
當我們再編寫配置文件時,已經給出了明確 deprecated 提示:
轉存失敗重新上傳取消轉存失敗重新上傳取消
總結
Spring Boot 的 @ConfigurationProperties
註解在綁定類型安全的 Java Bean 時是非常強大的,我們可以配合其註解屬性和 @DeprecatedConfigurationProperty
註解獲取到更友好的編程方式,同時這樣讓我們的配置更加模塊化。
附加說明
以爲 @ConfigurationProperties
註解滿足我們的全部需要了嗎?其實不然,Spring 官網明確給出了該註解和 @Value
註解的對比:
轉存失敗重新上傳取消轉存失敗重新上傳取消
如果使用 SpEL 表達式,我們只能選擇 @Value
註解。