1. 引言
Spring Boot 具有許多有用的功能,包括 外部化配置和從屬性文件中訪問定義的屬性。 以前的一篇教程描述瞭如何執行這些操作。
我們現在將更詳細地探討 <i@ConfigurationProperties> 註解。
2. 搭建環境
本教程採用標準化的搭建方式。我們首先在我們的 pom.xml 中添加 spring-boot-starter-parent</i/> 作為父項目:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.5.7</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>
<version>3.5.7</version>
</dependency>“入門使用 Hibernate Validator”頁面包含更多詳細信息。
3. 簡單屬性
官方文檔建議將配置屬性隔離到單獨的POJO中。
讓我們從這開始:
@Configuration
@ConfigurationProperties(prefix = "mail")
public class ConfigProperties {
private String hostName;
private int port;
private String from;
// standard getters and setters
}我們使用 @Configuration,以便 Spring 在應用程序上下文中創建一個 Spring Bean。
@ConfigurationProperties 最適合與具有相同前綴的層次化屬性一起使用;因此,我們添加了 mail 前綴。
Spring 框架使用標準的 Java Bean setter,因此我們必須聲明每個屬性的 setter。
注意:如果 POJO 中沒有使用 @Configuration,則需要在主 Spring 應用程序類中添加 @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
因此,我們可以使用以下屬性文件來設置所有字段:
#Simple properties
[email protected]
mail.port=9000
[email protected]
3.1. Spring Boot 2.2
自 Spring Boot 2.2 以來,Spring 通過 classpath 掃描查找並註冊 <em @ConfigurationProperties</em> 類。<em @ConfigurationProperties</em> 掃描需要顯式啓用,通過添加 <em @ConfigurationPropertiesScan</em> 註解即可。因此,我們無需使用 <em @Component</em> (以及其他元註解,如 <em @Configuration</em>) 註釋這些類,也不需要使用 <em @EnableConfigurationProperties</em>。
@ConfigurationProperties(prefix = "mail")
@ConfigurationPropertiesScan
public class ConfigProperties {
private String hostName;
private int port;
private String from;
// standard getters and setters
}
通過 @SpringBootApplication 啓用類路徑掃描器可以找到 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. 嵌套屬性
我們可以擁有嵌套屬性,存在於列表、映射和類中。
讓我們創建一個新的 憑據 類來使用嵌套屬性:
public class Credentials {
private String authMethod;
private String username;
private String password;
// standard getters and setters
}我們還需要更新 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;
// standard getters and setters
}以下屬性文件將設置所有字段:
#Simple properties
[email protected]
mail.port=9000
[email protected]
#List properties
mail.defaultRecipients[0][email protected]
mail.defaultRecipients[1][email protected]
#Map Properties
mail.additionalHeaders.redelivery=true
mail.additionalHeaders.secure=true
#Object properties
mail.credentials.username=john
mail.credentials.password=password
mail.credentials.authMethod=SHA15. 使用 @ConfigurationProperties 在 @Bean 方法上
我們也可以在 @Bean 註解的方法上使用 @ConfigurationProperties 註解。
這種方法在我們需要將屬性綁定到超出我們控制範圍的第三方組件時尤其有用。
讓我們創建一個簡單的 Item 類,我們將在此示例中使用它:
public class Item {
private String name;
private int size;
// standard getters and setters
}現在讓我們看看如何使用 @ConfigurationProperties 在 @Bean 方法中將外部化屬性綁定到 Item 實例:
@Configuration
public class ConfigProperties {
@Bean
@ConfigurationProperties(prefix = "item")
public Item item() {
return new Item();
}
}因此,任何以 item- 預綴命名的屬性都將映射到 Spring 上下文中管理的 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 獲取器和設置器,因此我們必須為每個屬性聲明獲取器和設置器。
7. 屬性轉換
@ConfigurationProperties 支持將屬性綁定到其對應的 Bean 上,支持多種類型的數據轉換。
7.1. 持續時間
我們將首先探討如何將屬性轉換為 持續時間 對象。
這裏有兩個類型為 持續時間 的字段:
@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=27.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)來支持將一個屬性轉換為特定的類類型。
讓我們添加一個簡單的類 Employee:
public class Employee {
private String name;
private double salary;
}然後,我們將創建一個自定義轉換器,用於轉換該屬性:
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. 不可變 <em @ConfigurationProperties</em> 綁定
自 Spring Boot 2.2 版本起,我們可以使用 <em @ConfigurationProperties</em> 標註 來綁定配置屬性,取代了舊的 setter 注入方式。
這意味着 <em @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 的,並且沒有 setter 方法。
此外,重要的是強調,
為了使用構造函數綁定,我們需要顯式地啓用我們的配置類,要麼使用 @EnableConfigurationProperties ,要麼使用 @ConfigurationPropertiesScan 。
9. Java 16 的 Record 類型
Java 16 引入了 Record 類型,作為 JEP 395 的一部分。Record 是一種作為不可變數據透明載體的類,非常適合作為配置持有者和 DTO 的使用。事實上,我們可以將 Java Record 定義為 Spring Boot 中的配置屬性。例如,之前的示例可以重寫為:
@ConstructorBinding
@ConfigurationProperties(prefix = "mail.credentials")
public record ImmutableCredentials(String authMethod, String username, String password) {
}相比之下,它更加簡潔,無需使用冗餘的 Getter 和 Setter 方法。
此外,自 Spring Boot 2.6 版本起 (Spring Boot 2.6),對於單構造函數記錄,我們無需使用 @ConstructorBinding 註解。
但是,如果我們的記錄具有多個構造函數,則仍應使用 @ConstructorBinding 註解來標識用於屬性綁定的構造函數。