Spring Boot 具有许多有用的功能,包括外部化配置和轻松访问属性文件中定义的属性。较早的教程描述了可以做到这一点的各种方法。
我们现在将更详细地探索*@ConfigurationProperties*注释。
本教程使用相当标准的设置。我们首先在pom.xml 中添加[spring-boot-starter-parent](https://search.maven.org/search?q=a:spring-boot-starter-parent AND g:org.springframework.boot)作为父级:
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.4.0version>
<relativePath/>
parent>
为了能够验证文件中定义的属性,我们还需要一个 JSR-303 的实现,[hibernate-validator](https://search.maven.org/search?q=a:hibernate-validator AND g:org.hibernate)就是其中之一。
让我们也将它添加到我们的pom.xml中:
<dependency>
<groupId>org.hibernategroupId>
<artifactId>hibernate-validatorartifactId>
<version>6.0.16.Finalversion>
dependency>
在“开始使用Hibernate验证”进行了详细介绍。
官方文档建议我们将配置属性隔离到单独的 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]
从Spring Boot 2.2 开始,Spring通过类路径扫描查找并注册@ConfigurationProperties*类*。因此,不需要使用@Component**(以及其他元注释,如@Configuration) 来注释此类类*,甚至不需要使用@EnableConfigurationProperties:***
@ConfigurationProperties(prefix = "mail")
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包中查找配置属性类。
我们可以在列表、地图和类中嵌套属性。**
让我们创建一个新的Credentials类来用于一些嵌套的属性:
public class Credentials {
private String authMethod;
private String username;
private String password;
// standard getters and setters
}
我们还需要更新ConfigProperties类使用列表,一个地图和凭证类:
public class ConfigProperties {
private String host;
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=SHA1
我们还可以在@Bean*注释方法上使用@ConfigurationProperties*注释。**
当我们想要将属性绑定到我们无法控制的第三方组件时,这种方法可能特别有用。
让我们创建一个简单的Item类,我们将在下一个示例中使用它:
public class Item {
private String name;
private int size;
// standard getters and setters
}
现在让我们看看如何在*@Bean方法上使用@ConfigurationProperties将外部化属性绑定到Item*实例:
@Configuration
public class ConfigProperties {
@Bean
@ConfigurationProperties(prefix = "item")
public Item item() {
return new Item();
}
}
因此,任何以 item 为前缀的属性都将映射到Spring 上下文管理的Item实例。
***@ConfigurationProperties*使用 JSR-303 格式提供属性验证。**这允许各种整洁的东西。
例如,让我们强制设置hostName属性:
@NotBlank
private String hostName;
接下来,让authMethod属性的长度为 1 到 4 个字符:
@Length(max = 4, min = 1)
private String authMethod;
然后端口属性从 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 很重要。
@ConfigurationProperties支持多种类型的转换,将属性绑定到相应的 bean。
我们将首先研究将属性转换为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
*同样,Spring Boot @ConfigurationProperties*支持*DataSize*类型转换。
让我们添加三个DataSize类型的字段:
private DataSize sizeInDefaultUnit;
private DataSize sizeInGB;
@DataSizeUnit(DataUnit.TERABYTES)
private DataSize sizeInTB;
这些是相应的属性:
conversion.sizeInDefaultUnit=300conversion.sizeInGB=2GBconversion.sizeInTB=4
在这种情况下,*sizeInDefaultUnit*值将为 300 字节,因为默认单位是字节。
支持的单位为B、KB、MB、GB和TB。我们还可以使用@DataSizeUnit覆盖默认单位*。*
我们还可以添加我们自己的自定义转换器来支持将属性转换为特定的类类型。
让我们添加一个简单的类Employee:
public class Employee { private String name; private double salary;}
然后我们将创建一个自定义转换器来转换此属性:
conversion.employee=john,2000
我们将其转换为Employee类型的文件:
private Employee employee;
我们需要实现Converter接口,然后使用*@ConfigurationPropertiesBinding*注释来注册我们的自定义*Converter:*
@Component@ConfigurationPropertiesBindingpublic 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])); }}
从 Spring Boot 2.2 开始,我们可以使用@ConstructorBinding*注释来绑定我们的配置属性*。
这实质上意味着*@ConfigurationProperties*注释的类现在可能是不可变的。
@ConfigurationProperties(prefix = "mail.credentials")@ConstructorBindingpublic class ImmutableCredentials { private final String authMethod; private final String username; private final String password; public ImmutableCredentials(String authMethod, String username, String password) { this.authMethod = authMethod; this.username = username; this.password = password; } public String getAuthMethod() { return authMethod; } public String getUsername() { return username; } public String getPassword() { return password; }}
正如我们所见,在使用 *@ConstructorBinding 时,*我们需要为构造函数提供我们想要绑定的所有参数。
请注意,ImmutableCredentials 的所有字段 都是最终的。此外,没有 setter 方法。
此外,需要强调的是,*要使用构造函数绑定,我们需要使用@EnableConfigurationProperties* 或 *@ConfigurationPropertiesScan***显式启用我们的配置类 。
在本文中,我们探讨了*@ConfigurationProperties*注释并重点介绍了它提供的一些有用功能,例如轻松绑定和 Bean 验证。
像往常一样,代码可以在 Github 上找到。