Spring Boot 中,若某類只用 @ConfigurationProperties 註解,然後該類:
- 沒有在掃描路徑下
- 或沒用 @Component 等註解
就會導致無法被掃描為 bean,須在配置類用 @EnableConfigurationProperties 註解去指定這個類,才能使 @ConfigurationProperties 生效,並作為一個 bean 新增進 Spring 容器。
@EnableConfigurationProperties 不能單獨使用:
- @EnableConfigurationProperties 和 @ConfigurationProperties組合使用
- @EnableConfigurationProperties將@ConfigurationProperties所修飾的類新增到IoC容器
1 簡介
Spring Boot外部化配置和輕鬆訪問 Properties 檔案中定義的屬性。上文介紹了實現這一點的各種方法。
本文看Spring Boot 的 @ConfigurationProperties
。
2 專案設定
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.0</version>
<relativePath/>
</parent>
為驗證檔案中定義的屬性,還需要一個 JSR-380 實現,hibernate-validator 就是其中之一,它由 spring-boot-starter-validation
依賴提供。
把它也新增到 pom.xml
中:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
更多詳細資訊可參閱 Hibernate Validator 入門。
3 示例屬性
官方建議將配置屬性隔離到單獨 POJO,如:
@Configuration
@ConfigurationProperties(prefix = "mail")
public class ConfigProperties {
private String hostName;
private int port;
private String from;
}
透過 @Configuration
,Spring 會在 Application Context 中建立一個 Spring Bean。
@ConfigurationProperties
適合具有相同字首的分層屬性。
Spring使用標準 Java Bean Setter,因此必須為每個屬性宣告Setter。
如不在 POJO 用 @Configuration
,則需在 Spring Application
main 類中新增 @EnableConfigurationProperties(ConfigProperties.class)
以將屬性繫結到 POJO:
@SpringBootApplication
@EnableConfigurationProperties(ConfigProperties.class)
public class EnableConfigurationDemoApplication {
public static void main(String[] args) {
SpringApplication.run(EnableConfigurationDemoApplication.class, args);
}
}
Spring 會自動繫結屬性檔案中定義的、字首為 mail
且名稱與 ConfigProperties
類中某個欄位相同的任何屬性。
Spring 對繫結屬性使用寬鬆規則,以下各種變體都可繫結到屬性 hostName
:
mail.hostName
mail.hostname
mail.host_name
mail.host-name
mail.HOST_NAME
可用如下 properties 檔案設定所有欄位:
# 示例 properties
mail.hostname=host@mail.com
mail.port=9000
mail.from=mailer@mail.com
3.1、Spring Boot 2.2
從 Spring Boot 2.2 開始,Spring 透過 classpath 掃描查詢並註冊 @ConfigurationProperties
類。對 @ConfigurationProperties
掃描需透過新增 @ConfigurationPropertiesScan
來明確選擇。
因此,不必用 @Component
(及 @Configuration
等其他元註解)註解這種類,甚至也不必用 @EnableConfigurationProperties
:
@ConfigurationProperties(prefix = "mail")
@ConfigurationPropertiesScan
public class ConfigProperties {
private String hostName;
private int port;
private String from;
}
@SpringBootApplication
啟用的 classpath Scanner 找到了 ConfigProperties
類,儘管我們沒有用 @Component
對該類進行註解。
還可用@ConfigurationPropertiesScan掃描自定義位置的配置屬性類:
@SpringBootApplication
@ConfigurationPropertiesScan("com.baeldung.configurationproperties")
public class EnableConfigurationDemoApplication {
public static void main(String[] args) {
SpringApplication.run(EnableConfigurationDemoApplication.class, args);
}
}
如上,Spring 將只在 com.baeldung.properties
包中查詢配置屬性類。
4 巢狀屬性
可在 List
、Map
和 Class
中巢狀屬性。
建立一個新的 Credentials
類,用於一些巢狀屬性:
public class Credentials {
private String authMethod;
private String username;
private String password;
//Get、Set 方法
}
還需要更新 ConfigProperties
類,以便使用 List
、Map
和 Credentials
類:
public class ConfigProperties {
private String hostname;
private int port;
private String from;
private List<String> defaultRecipients;
private Map<String, String> additionalHeaders;
private Credentials credentials;
// Get、Set 省略
}
下面的屬性檔案將設定所有欄位:
#Simple properties
mail.hostname=mailer@mail.com
mail.port=9000
mail.from=mailer@mail.com
#List properties
mail.defaultRecipients[0]=admin@mail.com
mail.defaultRecipients[1]=owner@mail.com
#Map Properties
mail.additionalHeaders.redelivery=true
mail.additionalHeaders.secure=true
#物件 properties
mail.credentials.username=john
mail.credentials.password=password
mail.credentials.authMethod=SHA1
5 @Bean 方法用 @ConfigurationProperties
當要將屬性繫結到無法控制的第三方元件時,特別有用。
public class Item {
private String name;
private int size;
}
看咋在 @Bean
方法中使用 @ConfigurationProperties
將外部化屬性繫結到 Item
例項:
@Configuration
public class ConfigProperties {
@Bean
@ConfigurationProperties(prefix = "item")
public Item item() {
return new Item();
}
}
任何 item
字首的屬性都將對映到 Spring Context 管理的 Item
例項。
6 屬性校驗
@ConfigurationProperties
使用 JSR-380 格式對屬性進行驗證。
例如,把 hostName
屬性成為強制性屬性:
@NotBlank
private String hostName;
接下來,把 authMethod
屬性的長度設為 1 到 4 個字元:
@Length(max = 4, min = 1)
private String authMethod;
然後,port
屬性是 1025 到 65536:
@Min(1025)
@Max(65536)
private int port;
最後,from
屬性必須與電子郵件地址格式相匹配:
@Pattern(regexp = "^[a-z0-9._%+-]+@[a-z0-9.-]+\\.[a-z]{2,6}$")
private String from;
這可以幫助減少程式碼中大量的 if - else 判斷條件,使程式碼看起來更簡潔明瞭。
如果其中任何一項驗證失敗,應用將無法啟動,並丟擲 IllegalStateException
異常。
Hibernate Validation 框架使用標準的 Java Bean Getter 和 Setter,因此必須為每個屬性宣告 Getter 和 Setter 方法。
7 屬性型別轉換
@ConfigurationProperties
支援多種型別的轉換,可將屬性繫結到相應的 Bean。
7.1 Duration
先來看看如何將屬性轉換為 Duration
物件。
這裡有兩個 Duration
型別的欄位:
@ConfigurationProperties(prefix = "conversion")
public class PropertyConversion {
private Duration timeInDefaultUnit;
private Duration timeInNano;
...
}
配置屬性如下:
conversion.timeInDefaultUnit=10
conversion.timeInNano=9ns
如上,欄位 timeInDefaultUnit
的值為 10 毫秒,而 timeInNano
的值為 9 納秒。
支援的單位有 ns、us、ms、s、m、h 和 d,分別表示納秒、微秒、毫秒、秒、分、小時和天。
預設單位是毫秒,這意味著如果不在數值旁邊指定單位,Spring 就會將數值轉換為毫秒。
還可以使用 @DurationUnit
來覆蓋預設單位:
@DurationUnit(ChronoUnit.DAYS)
private Duration timeInDays;
對應的屬性如下:
conversion.timeInDays=2
7.2、DataSize
同樣,Spring Boot @ConfigurationProperties
也支援 DataSize
型別轉換。
新增三個 DataSize
型別的欄位:
private DataSize sizeInDefaultUnit;
private DataSize sizeInGB;
@DataSizeUnit(DataUnit.TERABYTES)
private DataSize sizeInTB;
相應的屬性如下:
conversion.sizeInDefaultUnit=300
conversion.sizeInGB=2GB
conversion.sizeInTB=4
如上,sizeInDefaultUnit
的值是 300 位元組,因為預設單位是位元組。
支援的單位有 B、KB、MB、GB 和 TB。還可以使用 @DataSizeUnit
覆蓋預設單位。
7.3、自定義 Converter
還可以新增自己的自定義 Converter
,以支援將屬性轉換為特定的類型別。
新增一個簡單的 Employee
類:
public class Employee {
private String name;
private double salary;
}
然後,建立一個自定義 Converter
來轉換該屬性:
conversion.employee=john,2000
把它轉換為 Employee
型別:
private Employee employee;
需要實現 Converter
介面,然後使用 @ConfigurationPropertiesBinding
註解註冊自定義 Converter
:
@Component
@ConfigurationPropertiesBinding
public class EmployeeConverter implements Converter<String, Employee> {
@Override
public Employee convert(String from) {
String[] data = from.split(",");
return new Employee(data[0], Double.parseDouble(data[1]));
}
}
8 不可變的 @ConfigurationProperties 繫結
從 Spring Boot 2.2 開始,可以使用 @ConstructorBinding
註解來繫結配置屬性,而不是老式的 Setter 注入。
這基本上意味著 @ConfigurationProperties
註解的類現在可以是不可變的了。
在 Spring Boot 3 中,如果只有一個帶參建構函式,那麼建構函式繫結就是隱式的,不需要使用註解。但如果有多個建構函式,必須註解首選的那個:
@ConfigurationProperties(prefix = "mail.credentials")
public class ImmutableCredentials {
private final String authMethod;
private final String username;
private final String password;
@ConstructorBinding
public ImmutableCredentials(String authMethod, String username, String password) {
this.authMethod = authMethod;
this.username = username;
this.password = password;
}
public ImmutableCredentials(String username, String password) {
this.username = username;
this.password = password;
this.authMethod = "Default";
}
public String getAuthMethod() {
return authMethod;
}
public String getUsername() {
return username;
}
public String getPassword() {
return password;
}
}
如上,在使用 @ConstructorBinding
時,需要為建構函式提供想要繫結的所有引數。
注意,ImmutableCredentials
的所有欄位都是 final
欄位。此外,ImmutableCredentials
沒有 Setter 方法。
此外,需要強調的是,要使用建構函式繫結,需要透過 @EnableConfigurationProperties
或 @ConfigurationPropertiesScan
明確啟用配置類。
9 Java 16 Record
Java 16 引入 Record
型別(JEP 395)。Record
類是不可變資料的透明載體。這使它們成為配置持有者和 DTO 的理想選擇。事實上,可以在 Spring Boot 中將 Java Record
定義為配置屬性。例如,前面的示例可重寫為:
@ConstructorBinding
@ConfigurationProperties(prefix = "mail.credentials")
public record ImmutableCredentials(String authMethod, String username, String password) {
}
顯然,它比那些模板式的 Getter 和 Setter 更簡潔。
Spring Boot 2.6 開始,對於單建構函式 Record
,可以不用 @ConstructorBinding
註解。但是,如果 Record
有多個建構函式,則仍應使用 @ConstructorBinding
來標識用於屬性繫結的建構函式。
10 總結
本文介紹瞭如何在 Spring Boot 中使用 @ConfigurationProperties
來繫結配置屬性,以及如何進行屬性驗證和屬性轉換。
關注我,緊跟本系列專欄文章,咱們下篇再續!
作者簡介:魔都架構師,多家大廠後端一線研發經驗,在分散式系統設計、資料平臺架構和AI應用開發等領域都有豐富實踐經驗。
各大技術社群頭部專家博主。具有豐富的引領團隊經驗,深厚業務架構和解決方案的積累。
負責:
- 中央/分銷預訂系統效能最佳化
- 活動&券等營銷中臺建設
- 交易平臺及資料中臺等架構和開發設計
- 車聯網核心平臺-物聯網連線平臺、大資料平臺架構設計及最佳化
- LLM Agent應用開發
- 區塊鏈應用開發
- 大資料開發挖掘經驗
- 推薦系統專案
目前主攻市級軟體專案設計、構建服務全社會的應用系統。
參考:
- 程式設計嚴選網
本文由部落格一文多發平臺 OpenWrite 釋出!