@ConfigurationProperties 註解使用姿式,這一篇就夠了

在編寫項目代碼時,咱們要求更靈活的配置,更好的模塊化整合。在 Spring Boot 項目中,爲知足以上要求,咱們將大量的參數配置在 application.properties 或 application.yml 文件中,經過 @ConfigurationProperties 註解,咱們能夠方便的獲取這些參數值html

使用 @ConfigurationProperties 配置模塊

假設咱們正在搭建一個發送郵件的模塊。在本地測試,咱們不想該模塊真的發送郵件,因此咱們須要一個參數來「開關」 disable 這個功能。另外,咱們但願爲這些郵件配置一個默認的主題,這樣,當咱們查看郵件收件箱,經過郵件主題能夠快速判斷出這是測試郵件java

在 application.properties 文件中建立這些參數:git

咱們可使用 @Value 註解或着使用 Spring Environment bean 訪問這些屬性,是這種注入配置方式有時顯得很笨重。咱們將使用更安全的方式(@ConfigurationProperties )來獲取這些屬性github

@ConfigurationProperties 的基本用法很是簡單:咱們爲每一個要捕獲的外部屬性提供一個帶有字段的類。請注意如下幾點:面試

  • 前綴定義了哪些外部屬性將綁定到類的字段上
  • 根據 Spring Boot 寬鬆的綁定規則,類的屬性名稱必須與外部屬性的名稱匹配
  • 咱們能夠簡單地用一個值初始化一個字段來定義一個默認值
  • 類自己能夠是包私有的
  • 類的字段必須有公共 setter 方法

Spring 寬鬆綁定規則 (relaxed binding)

Spring使用一些寬鬆的綁定屬性規則。所以,如下變體都將綁定到 hostName 屬性上:spring

若是咱們將 MailModuleProperties 類型的 bean 注入到另外一個 bean 中,這個 bean 如今能夠以類型安全的方式訪問那些外部配置參數的值。shell

可是,咱們仍然須要讓 Spring 知道咱們的 @ConfigurationProperties 類存在,以便將其加載到應用程序上下文中( 面試還不知道 BeanFactory 和 ApplicationContext 的區別?)編程

激活 @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 標記,若是驗證失敗,方法拋出異常便可, 關於 @PostConstruct,能夠查看 Spring Bean 的生命週期,我從哪裏來?

複雜屬性類型

多數狀況,咱們傳遞給應用的參數是基本的字符串或數字。可是,有時咱們須要傳遞諸如 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 bytes
  • KB for kilobytes
  • MB for megabytes
  • GB for gigabytes
  • TB 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 註解

另外我以前在閱讀 RabbitMQ 源碼時,發現 RabbitProperties 類充分的利用了 @ConfigurationProperties 註解特性:

  • deprecated

  • Duration

  • Enum
  • 嵌套屬性
感受本身後知後覺,最近在思考,爲何小時候要閱讀和背誦古詩詞,文言文等經典,由於這樣寫文章就能夠輕鬆熟練的引用經典。技術也同樣,各類框架的源碼就是學生時代的古詩詞和文言文,咱們要多多查看閱讀,甚至背誦編程思想,這樣就能夠寫出愈來愈優雅的代碼

關於 @ConfigurationProperties 註解的使用,這裏推薦 RabbitMQ Github 源碼,只需看這一個類就能夠,知道怎樣充分利用這個註解.

Demo 代碼獲取,回覆公衆號「demo」,打開連接查看對應的子文件夾便可

靈魂追問

  1. 在實際項目中, 你可以充分利用這些特性讓你的配置更靈活和模塊化嗎?
  2. 閱讀框架源碼時,他們都是怎樣配置的呢?
  3. @Value 註解怎樣給出默認值?
以讀偵探小說思惟輕鬆趣味學習 Java 技術棧相關知識,本着將複雜問題簡單化,抽象問題具體化和圖形化原則逐步分解技術問題,技術持續更新,請持續關注......

提升效率工具

[center]


推薦閱讀


歡迎持續關注公衆號:「日拱一兵」

  • 前沿 Java 技術乾貨分享
  • 高效工具彙總 | 回覆「工具」
  • 面試問題分析與解答
  • 技術資料領取 | 回覆「資料」

以讀偵探小說思惟輕鬆趣味學習 Java 技術棧相關知識,本着將複雜問題簡單化,抽象問題具體化和圖形化原則逐步分解技術問題,技術持續更新,請持續關注......

相關文章
相關標籤/搜索