spring入門:常用的註解(讀取配置文件)

關鍵字:@ConfigurationProperties(prefix="")、@EnableConfigurationProperties(TestClass.class)、@Value、@PropertySource(value={"classpath:test.properties"})

前言

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

 使用 @ConfigurationProperties 配置模塊

假設我們正在搭建一個發送郵件的模塊。在本地測試,我們不想該模塊真的發送郵件,所以我們需要一個參數來「開關」 disable 這個功能。另外,我們希望爲這些郵件配置一個默認的主題,這樣,當我們查看郵件收件箱,通過郵件主題可以快速判斷出這是測試郵件

在 application.properties 文件中創建這些參數:
2019-07-24-16-24-05%402x.pnguploading.4e448015.gif轉存失敗重新上傳取消2019-07-24-16-24-05%402x.pnguploading.4e448015.gif轉存失敗重新上傳取消

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

2019-07-24-16-24-34%402x.pnguploading.4e448015.gif轉存失敗重新上傳取消2019-07-24-16-24-34%402x.pnguploading.4e448015.gif轉存失敗重新上傳取消

@ConfigurationProperties 的基本用法非常簡單:我們爲每個要捕獲的外部屬性提供一個帶有字段的類。請注意以下幾點:

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

Spring 寬鬆綁定規則 (relaxed binding)

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

2019-07-24-16-27-10%402x.pnguploading.4e448015.gif轉存失敗重新上傳取消2019-07-24-16-27-10%402x.pnguploading.4e448015.gif轉存失敗重新上傳取消

如果我們將 MailModuleProperties 類型的 bean 注入到另一個 bean 中,這個 bean 現在可以以類型安全的方式訪問那些外部配置參數的值。

但是,我們仍然需要讓 Spring 知道我們的 @ConfigurationProperties 類存在,以便將其加載到應用程序上下文中。

激活 @ConfigurationProperties

對於 Spring Boot,創建一個 MailModuleProperties 類型的 bean,我們可以通過下面幾種方式將其添加到應用上下文中

首先,我們可以通過添加 @Component 註解讓 Component Scan 掃描到
2019-07-24-16-27-33%402x.pnguploading.4e448015.gif轉存失敗重新上傳取消2019-07-24-16-27-33%402x.pnguploading.4e448015.gif轉存失敗重新上傳取消

很顯然,只有當類所在的包被 Spring @ComponentScan 註解掃描到纔會生效,默認情況下,該註解會掃描在主應用類下的所有包結構

我們也可以通過 Spring 的 Java Configuration 特性實現同樣的效果:
2019-07-24-16-29-20%402x.pnguploading.4e448015.gif轉存失敗重新上傳取消2019-07-24-16-29-20%402x.pnguploading.4e448015.gif轉存失敗重新上傳取消

只要 MailModuleConfiguration 類被 Spring Boot 應用掃描到,我們就可以在應用上下文中訪問 MailModuleProperties bean

我們還可以使用 @EnableConfigurationProperties 註解讓我們的類被 Spring Boot 所知道,在該註解中其實是用了@Import(EnableConfigurationPropertiesImportSelector.class) 實現,大家可以看一下
2019-07-24-16-29-42%402x.pnguploading.4e448015.gif轉存失敗重新上傳取消2019-07-24-16-29-42%402x.pnguploading.4e448015.gif轉存失敗重新上傳取消

激活一個 @ConfigurationProperties 類的最佳方式是什麼?

所有上述方法都同樣有效。然而,我建議模塊化你的應用程序,並讓每個模塊提供自己的@ConfigurationProperties 類,只提供它需要的屬性,就像我們在上面的代碼中對郵件模塊所做的那樣。這使得在不影響其他模塊的情況下重構一個模塊中的屬性變得容易。

因此,我不建議在應用程序類本身上使用 @EnableConfigurationProperties,如許多其他教程中所示,是在特定於模塊的 @Configuration 類上使用@EnableConfigurationProperties,該類也可以利用包私有的可見性對應用程序的其餘部分隱藏屬性。

無法轉換的屬性

如果我們在 application.properties 屬性上定義的屬性不能被正確的解析會發生什麼?假如我們爲原本應該爲布爾值的屬性提供的值爲 'foo':
2019-07-24-16-30-09%402x.pnguploading.4e448015.gif轉存失敗重新上傳取消2019-07-24-16-30-09%402x.pnguploading.4e448015.gif轉存失敗重新上傳取消

默認情況下,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)
2019-07-24-16-49-56%402x.pnguploading.4e448015.gif轉存失敗重新上傳取消2019-07-24-16-49-56%402x.pnguploading.4e448015.gif轉存失敗重新上傳取消

這樣,Spring Boot 將會設置 enabled 字段爲我們在 Java 代碼裏設定好的默認值。如果我們沒有設置默認值,enabled 將爲 null,因爲這裏定義的是 boolean 的包裝類 Boolean

未知的屬性

和上面的情況有些相反,如果我們在 application.properties 文件提供了 MailModuleProperties 類不知道的屬性會發生什麼?
2019-07-24-16-52-20%402x.pnguploading.4e448015.gif轉存失敗重新上傳取消2019-07-24-16-52-20%402x.pnguploading.4e448015.gif轉存失敗重新上傳取消

默認情況下,Spring Boot 會忽略那些不能綁定到 @ConfigurationProperties 類字段的屬性

然而,當配置文件中有一個屬性實際上沒有綁定到 @ConfigurationProperties 類時,我們可能希望啓動失敗。也許我們以前使用過這個配置屬性,但是它已經被刪除了,這種情況我們希望被觸發告知手動從 application.properties 刪除這個屬性

爲了實現上述情況,我們僅需要將 ignoreUnknownFields 屬性設置爲 false (默認是 true)
2019-07-24-16-53-28%402x.pnguploading.4e448015.gif轉存失敗重新上傳取消2019-07-24-16-53-28%402x.pnguploading.4e448015.gif轉存失敗重新上傳取消

現在,應用啓動時,控制檯會反饋我們異常信息

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 註解
2019-07-24-16-55-40%402x.pnguploading.4e448015.gif轉存失敗重新上傳取消2019-07-24-16-55-40%402x.pnguploading.4e448015.gif轉存失敗重新上傳取消

如果我們忘記在 application.properties 文件設置 enabled 屬性,並且設置 defaultSubject 爲空
2019-07-24-16-56-53%402x.pnguploading.4e448015.gif轉存失敗重新上傳取消2019-07-24-16-56-53%402x.pnguploading.4e448015.gif轉存失敗重新上傳取消

應用啓動時,我們將會得到 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 類中
2019-07-24-16-58-11%402x.pnguploading.4e448015.gif轉存失敗重新上傳取消2019-07-24-16-58-11%402x.pnguploading.4e448015.gif轉存失敗重新上傳取消

我們有兩種方式讓 Spring Boot 自動填充該 list 屬性

application.properties

在 application.properties 文件中以數組形式書寫
2019-07-24-16-59-06%402x.pnguploading.4e448015.gif轉存失敗重新上傳取消2019-07-24-16-59-06%402x.pnguploading.4e448015.gif轉存失敗重新上傳取消

application.yml

YAML 本身支持 list 類型,所以可以在 application.yml 文件中添加:
2019-07-24-16-59-34%402x.pnguploading.4e448015.gif轉存失敗重新上傳取消2019-07-24-16-59-34%402x.pnguploading.4e448015.gif轉存失敗重新上傳取消

set 集合也是這種方式的配置方式,不再重複書寫。另外YAML 是更好的閱讀方式,層次分明,所以在實際應用中更推薦大家使用該種方式做數據配置

Duration

Spring Boot 內置支持從配置參數中解析 durations (持續時間),官網文檔 給出了明確的說明
2019-07-24-17-00-08%402x.pnguploading.4e448015.gif轉存失敗重新上傳取消2019-07-24-17-00-08%402x.pnguploading.4e448015.gif轉存失敗重新上傳取消

我們既可以配置毫秒數數值,也可配置帶有單位的文本:
2019-07-24-17-01-01%402x.pnguploading.4e448015.gif轉存失敗重新上傳取消2019-07-24-17-01-01%402x.pnguploading.4e448015.gif轉存失敗重新上傳取消

官網上已明確說明,配置 duration 不寫單位,默認按照毫秒來指定,我們也可已通過 @DurationUnit 來指定單位:
2019-07-24-17-01-47%402x.pnguploading.4e448015.gif轉存失敗重新上傳取消2019-07-24-17-01-47%402x.pnguploading.4e448015.gif轉存失敗重新上傳取消

常用單位如下:

  • 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 單位指定:
2019-07-24-17-02-34%402x.pnguploading.4e448015.gif轉存失敗重新上傳取消2019-07-24-17-02-34%402x.pnguploading.4e448015.gif轉存失敗重新上傳取消

添加配置
2019-07-24-17-03-17%402x.pnguploading.4e448015.gif轉存失敗重新上傳取消2019-07-24-17-03-17%402x.pnguploading.4e448015.gif轉存失敗重新上傳取消

但是,我測試的時候打印出來結果都是以 B (bytes) 來顯示

常見單位如下:

  • B for bytes
  • KB for kilobytes
  • MB for megabytes
  • GB for gigabytes
  • TB for terabytes

自定義類型

有些情況,我們想解析配置參數到我們自定義的對象類型上,假設,我們我們設置最大包裹重量:
2019-07-24-17-04-08%402x.pnguploading.4e448015.gif轉存失敗重新上傳取消2019-07-24-17-04-08%402x.pnguploading.4e448015.gif轉存失敗重新上傳取消

在 MailModuleProperties 中添加 Weight 屬性
2019-07-24-17-04-51%402x.pnguploading.4e448015.gif轉存失敗重新上傳取消2019-07-24-17-04-51%402x.pnguploading.4e448015.gif轉存失敗重新上傳取消

我們可以模仿 DataSize 和 Duration 創造自己的 converter (轉換器)
2019-07-24-17-05-39%402x.pnguploading.4e448015.gif轉存失敗重新上傳取消2019-07-24-17-05-39%402x.pnguploading.4e448015.gif轉存失敗重新上傳取消

將其註冊到 Spring Boot 上下文中
2019-07-24-17-07-03%402x.pnguploading.4e448015.gif轉存失敗重新上傳取消2019-07-24-17-07-03%402x.pnguploading.4e448015.gif轉存失敗重新上傳取消

@ConfigurationPropertiesBinding 註解是讓 Spring Boot 知道使用該轉換器做數據綁定

使用 Spring Boot Configuration Processor 完成自動補全

我們向項目中添加依賴:

Maven

2019-07-24-17-09-28%402x.pnguploading.4e448015.gif轉存失敗重新上傳取消2019-07-24-17-09-28%402x.pnguploading.4e448015.gif轉存失敗重新上傳取消

Gradle

2019-07-24-17-09-58%402x.pnguploading.4e448015.gif轉存失敗重新上傳取消2019-07-24-17-09-58%402x.pnguploading.4e448015.gif轉存失敗重新上傳取消

重新 build 項目之後,configuration processor 會爲我們創建一個 JSON 文件:
2019-07-24-15-23-19.pnguploading.4e448015.gif轉存失敗重新上傳取消2019-07-24-15-23-19.pnguploading.4e448015.gif轉存失敗重新上傳取消

這樣,當我們在 application.properties 和 application.yml 中寫配置的時候會有自動提醒:
2019-07-24-15-24-36.pnguploading.4e448015.gif轉存失敗重新上傳取消2019-07-24-15-24-36.pnguploading.4e448015.gif轉存失敗重新上傳取消

標記配置屬性爲 Deprecated

configuration processor 允許我們標記某一個屬性爲 deprecated
2019-07-24-17-11-03%402x.pnguploading.4e448015.gif轉存失敗重新上傳取消2019-07-24-17-11-03%402x.pnguploading.4e448015.gif轉存失敗重新上傳取消

我們可以通過添加 @DeprecatedConfigurationProperty 註解到字段的 getter 方法上,來標示該字段爲 deprecated,重新 build 項目,看看 JSON 文件發生了什麼?
2019-07-24-17-12-06%402x.pnguploading.4e448015.gif轉存失敗重新上傳取消2019-07-24-17-12-06%402x.pnguploading.4e448015.gif轉存失敗重新上傳取消

當我們再編寫配置文件時,已經給出了明確 deprecated 提示:
2019-07-24-15-52-19.pnguploading.4e448015.gif轉存失敗重新上傳取消2019-07-24-15-52-19.pnguploading.4e448015.gif轉存失敗重新上傳取消

總結

Spring Boot 的 @ConfigurationProperties 註解在綁定類型安全的 Java Bean 時是非常強大的,我們可以配合其註解屬性和 @DeprecatedConfigurationProperty 註解獲取到更友好的編程方式,同時這樣讓我們的配置更加模塊化。

附加說明

以爲 @ConfigurationProperties 註解滿足我們的全部需要了嗎?其實不然,Spring 官網明確給出了該註解和 @Value 註解的對比:
2019-07-24-16-02-04.pnguploading.4e448015.gif轉存失敗重新上傳取消2019-07-24-16-02-04.pnguploading.4e448015.gif轉存失敗重新上傳取消

如果使用 SpEL 表達式,我們只能選擇 @Value 註解。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章