在Spring Boot中,@ConfigurationProperties注解用于将配置参数(通常来自properties或YAML文件)绑定到一个Java类中。但是,你知道吗,你还可以使用@ConfigurationProperties注解在Spring应用程序启动时验证配置属性吗?让我们深入了解一下!
教程
假设我们有一个Spring Boot的导入器服务,该服务定期将客户数据从CSV文件导入到数据库中。为了使导入器正常工作,我们需要以下配置:
importer:
service:
filePath: '/nfs/files'
fileType: '.xslx'
threadPoolSize: 3
可以使用@ConfigurationProperties注解将这些属性自动映射到一个Java类中,如下所示:
@Getter
@Setter
@Component
@ConfigurationProperties(prefix = "importer.service")
public class ImporterConfig {
String filePath;
String fileType;
int threadPoolSize;
}
现在,假设我们希望确保在应用程序启动时提供导入器配置并符合验证标准。如果验证中有任何失败,那么应用程序应在启动过程中失败。
为使此验证有效,我们需要在类路径上拥有一个JSR-380实现,如Hibernate Validator。
implementation 'org.springframework.boot:spring-boot-starter-validation'
一旦我们在类路径上有了一个验证器,我们需要使用@Validated注解显式启用配置属性验证。
例如:
@Getter
@Setter
@Component
@Validated
@ConfigurationProperties(prefix = "importer.service")
public class ImporterConfig {
@NotNull
String filePath;
@NotNull
@Pattern(regexp = "\\.csv$|\\.txt$")
String fileType;
@Positive
@Max(10)
int threadPoolSize;
}
与JSR-380的bean验证注解(例如@Max和@NotNull)一起使用时,这可以在应用程序启动时验证属性,下面是一些用于bean验证的常见注解:
@NotNull:指定属性不能为null@NotEmpty:指定属性不能为空或不为空@Size:确保属性大小在属性min和max之间@Email:指定属性必须是有效的电子邮件地址@AssertTrue/@AssertFalse:确保属性值为true/false@Positive/@Negative:确保属性为正数/负数@Past/@Future:指定日期必须是过去/未来@Max/@Min:指定属性的值不大于/不小于值属性
JSR 380中内置的完整验证约束列表可在此处找到。
现在运行应用程序,注意它将会将这些属性application.yaml绑定到ImporterConfig对象,并在应用程序启动时验证它们,从而确保应用程序不会以无效配置运行。
如果验证失败,它将会导致BindException,如下所示:
***************************
APPLICATION FAILED TO START
***************************
Description:
Binding to target com.stacktips.app.config.ImporterConfig failed:
Property: importer.service.fileType
Value: ".xlsx"
Origin: class path resource [application.yaml] - 8:15
Reason: must match "\.csv$|\.txt$"
Action:
Update your application's configuration
现在,更新fileType: .csv,应用程序将会如预期启动。
使用Record的注意事项
从Spring Boot 2.6和Java 16开始,您可以使用记录类与@ConfigurationProperties。但在使用记录时,您需要考虑一些事项。
@Validated
@ConfigurationProperties(prefix = "importer.service")
public record ImporterProperties(
@NotNull String filePath,
@Min(3) int threadPoolSize) {
}
@Componenet注解不能与记录一起使用。你的IDE将显示错误:annotated with @ConstructorBinding but defined as a Spring component。
这是因为@Component注解通常用于定义一个Spring容器将自动检测和管理的bean。由于Java记录是不可变的,这与Spring管理bean的方式冲突,该方式涉及代理、拦截和潜在地更改对象状态或行为。
为了解决这个问题,我们需要移除@Component注解,并显式定义bean注册。我们可以使用@EnableConfigurationProperties或在其中一个@Configuration类中定义@Bean注解。
例如:
@EnableConfigurationProperties(ImporterProperties.class)
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
视频