Java Bean 验证
Spring 框架为 Java Bean Validation API 提供支持。
Bean Validation 概述
Bean Validation 提供了一种通用的验证方式,通过约束声明和元数据为 Java 应用程序提供验证。要使用它,您需要使用声明式验证约束对域模型属性进行注释,然后在运行时强制执行这些约束。有内置约束,您还可以定义自己的自定义约束。
考虑以下示例,它显示了一个简单的 PersonForm
模型,其中有两个属性
-
Java
-
Kotlin
public class PersonForm {
private String name;
private int age;
}
class PersonForm(
private val name: String,
private val age: Int
)
Bean Validation 允许您声明约束,如下例所示
-
Java
-
Kotlin
public class PersonForm {
@NotNull
@Size(max=64)
private String name;
@Min(0)
private int age;
}
class PersonForm(
@get:NotNull @get:Size(max=64)
private val name: String,
@get:Min(0)
private val age: Int
)
Bean Validation 验证器然后根据声明的约束验证此类的实例。有关 API 的一般信息,请参阅 Bean Validation。有关特定约束的信息,请参阅 Hibernate Validator 文档。要了解如何将 Bean 验证提供程序设置为 Spring Bean,请继续阅读。
配置 Bean 验证提供程序
Spring 为 Bean Validation API 提供了完全支持,包括将 Bean Validation 提供程序引导为 Spring Bean。这使您可以在应用程序中需要验证的任何地方注入 jakarta.validation.ValidatorFactory
或 jakarta.validation.Validator
。
您可以使用 LocalValidatorFactoryBean
将默认验证器配置为 Spring Bean,如下例所示
-
Java
-
XML
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
@Configuration
public class AppConfig {
@Bean
public LocalValidatorFactoryBean validator() {
return new LocalValidatorFactoryBean();
}
}
<bean id="validator"
class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/>
上例中的基本配置触发 Bean 验证使用其默认引导机制进行初始化。Bean 验证提供程序(例如 Hibernate Validator)应存在于类路径中,并自动检测到。
注入 Jakarta 验证器
LocalValidatorFactoryBean
同时实现了 jakarta.validation.ValidatorFactory
和 jakarta.validation.Validator
,因此您可以注入对后者的引用以应用验证逻辑,如果您希望直接使用 Bean 验证 API,如下例所示
-
Java
-
Kotlin
import jakarta.validation.Validator;
@Service
public class MyService {
@Autowired
private Validator validator;
}
import jakarta.validation.Validator;
@Service
class MyService(@Autowired private val validator: Validator)
注入 Spring 验证器
除了实现 jakarta.validation.Validator
之外,LocalValidatorFactoryBean
还适用于 org.springframework.validation.Validator
,因此如果您的 Bean 需要 Spring 验证 API,您可以注入对后者的引用。
例如
-
Java
-
Kotlin
import org.springframework.validation.Validator;
@Service
public class MyService {
@Autowired
private Validator validator;
}
import org.springframework.validation.Validator
@Service
class MyService(@Autowired private val validator: Validator)
当用作 org.springframework.validation.Validator
时,LocalValidatorFactoryBean
调用底层的 jakarta.validation.Validator
,然后将 ContraintViolation
转换为 FieldError
,并将其注册到传递给 validate
方法的 Errors
对象中。
配置自定义约束
每个 Bean 验证约束包含两部分
-
声明约束及其可配置属性的
@Constraint
注释。 -
实现约束行为的
jakarta.validation.ConstraintValidator
接口的实现。
为了将声明与实现关联,每个 @Constraint
注释都引用相应的 ConstraintValidator
实现类。在运行时,当在您的域模型中遇到约束注释时,ConstraintValidatorFactory
会实例化引用的实现。
默认情况下,LocalValidatorFactoryBean
配置一个 SpringConstraintValidatorFactory
,该工厂使用 Spring 创建 ConstraintValidator
实例。这使您的自定义 ConstraintValidators
可以像任何其他 Spring Bean 一样受益于依赖注入。
以下示例显示了一个自定义 @Constraint
声明,后面是一个关联的 ConstraintValidator
实现,该实现使用 Spring 进行依赖注入
-
Java
-
Kotlin
@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy=MyConstraintValidator.class)
public @interface MyConstraint {
}
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.FIELD)
@Retention(AnnotationRetention.RUNTIME)
@Constraint(validatedBy = MyConstraintValidator::class)
annotation class MyConstraint
-
Java
-
Kotlin
import jakarta.validation.ConstraintValidator;
public class MyConstraintValidator implements ConstraintValidator {
@Autowired;
private Foo aDependency;
// ...
}
import jakarta.validation.ConstraintValidator
class MyConstraintValidator(private val aDependency: Foo) : ConstraintValidator {
// ...
}
如上例所示,ConstraintValidator
实现可以像任何其他 Spring Bean 一样 @Autowired
其依赖项。
Spring 驱动的 Method 验证
您可以通过 MethodValidationPostProcessor
Bean 定义将 Bean 验证的方法验证功能集成到 Spring 上下文中
-
Java
-
XML
import org.springframework.validation.beanvalidation.MethodValidationPostProcessor;
@Configuration
public class AppConfig {
@Bean
public MethodValidationPostProcessor validationPostProcessor() {
return new MethodValidationPostProcessor();
}
}
<bean class="org.springframework.validation.beanvalidation.MethodValidationPostProcessor"/>
为了符合 Spring 驱动的 Method 验证的条件,目标类需要使用 Spring 的 @Validated
注释进行注释,该注释还可以选择声明要使用的验证组。有关使用 Hibernate Validator 和 Bean 验证提供程序进行设置的详细信息,请参阅 MethodValidationPostProcessor
。
Spring MVC 和 WebFlux 对相同的基础方法验证内置了支持,但不需要 AOP。因此,请检查本节的其余内容,并参阅 Spring MVC 验证和 错误响应部分,以及 WebFlux 验证和 错误响应部分。
方法验证异常
默认情况下,jakarta.validation.ConstraintViolationException
会使用jakarata.validation.Validator
返回的ConstraintViolation
集合引发。或者,您可以改为使用已调整为MessageSourceResolvable
错误的ConstraintViolation
引发MethodValidationException
。要启用,请设置以下标志
-
Java
-
XML
import org.springframework.validation.beanvalidation.MethodValidationPostProcessor;
@Configuration
public class AppConfig {
@Bean
public MethodValidationPostProcessor validationPostProcessor() {
MethodValidationPostProcessor processor = new MethodValidationPostProcessor();
processor.setAdaptConstraintViolations(true);
return processor;
}
}
<bean class="org.springframework.validation.beanvalidation.MethodValidationPostProcessor">
<property name="adaptConstraintViolations" value="true"/>
</bean>
MethodValidationException
包含一个ParameterValidationResult
列表,该列表按方法参数对错误进行分组,每个列表都公开一个MethodParameter
、参数值以及一个从ConstraintViolation
调整而来的MessageSourceResolvable
错误列表。对于具有字段和属性级联违规的@Valid
方法参数,ParameterValidationResult
是ParameterErrors
,它实现了org.springframework.validation.Errors
并将验证错误公开为FieldError
。
自定义验证错误
调整后的MessageSourceResolvable
错误可以通过配置的MessageSource
以及特定于语言环境和语言的资源包转换为要向用户显示的错误消息。本节提供了一个示例来说明。
给定以下类声明
-
Java
-
Kotlin
record Person(@Size(min = 1, max = 10) String name) {
}
@Validated
public class MyService {
void addStudent(@Valid Person person, @Max(2) int degrees) {
// ...
}
}
@JvmRecord
internal data class Person(@Size(min = 1, max = 10) val name: String)
@Validated
class MyService {
fun addStudent(person: @Valid Person?, degrees: @Max(2) Int) {
// ...
}
}
Person.name()
上的ConstraintViolation
调整为具有以下内容的FieldError
-
错误代码
"Size.student.name"
、"Size.name"
、"Size.java.lang.String"
和"Size"
-
消息参数
"name"
、10
和1
(字段名称和约束属性) -
默认消息“大小必须介于 1 和 10 之间”
要自定义默认消息,可以使用任何上述错误代码和消息参数将属性添加到 MessageSource 资源包中。还要注意,消息参数 "name"
本身是一个 MessagreSourceResolvable
,其错误代码为 "student.name"
和 "name"
,也可以自定义。例如
- 属性
-
Size.student.name=Please, provide a {0} that is between {2} and {1} characters long student.name=username
degrees
方法参数上的 ConstraintViolation
适用于具有以下内容的 MessageSourceResolvable
-
错误代码
"Max.myService#addStudent.degrees"
、"Max.degrees"
、"Max.int"
、"Max"
-
消息参数“degrees2 和 2(字段名称和约束属性)
-
默认消息“必须小于或等于 2”
要自定义上述默认消息,可以添加一个属性,例如
- 属性
-
Max.degrees=You cannot provide more than {1} {0}
其他配置选项
默认的 LocalValidatorFactoryBean
配置足以满足大多数情况。对于各种 Bean 验证结构,从消息插值到遍历解析,都有许多配置选项。请参阅 LocalValidatorFactoryBean
javadoc 以获取有关这些选项的更多信息。
配置 DataBinder
可以使用 Validator
配置 DataBinder
实例。配置后,可以通过调用 binder.validate()
来调用 Validator
。任何验证 Errors
都会自动添加到 binder 的 BindingResult
中。
以下示例展示了如何以编程方式使用 DataBinder
在绑定到目标对象后调用验证逻辑
-
Java
-
Kotlin
Foo target = new Foo();
DataBinder binder = new DataBinder(target);
binder.setValidator(new FooValidator());
// bind to the target object
binder.bind(propertyValues);
// validate the target object
binder.validate();
// get BindingResult that includes any validation errors
BindingResult results = binder.getBindingResult();
val target = Foo()
val binder = DataBinder(target)
binder.validator = FooValidator()
// bind to the target object
binder.bind(propertyValues)
// validate the target object
binder.validate()
// get BindingResult that includes any validation errors
val results = binder.bindingResult
还可以通过 dataBinder.addValidators
和 dataBinder.replaceValidators
使用多个 Validator
实例配置 DataBinder
。当将全局配置的 Bean 验证与在 DataBinder 实例上本地配置的 Spring Validator
相结合时,这非常有用。请参阅 Spring MVC 验证配置。
Spring MVC 3 验证
请参阅 Spring MVC 章节中的 验证。