|
对于最新的稳定版本,请使用 Spring Framework 7.0.6! |
Java Bean验证
Spring框架为 Java Bean验证 API提供了支持。
Bean验证概述
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 将默认的 Validator 配置为 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 Validation 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 Validation 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,然后将 ConstraintViolation 适配为 FieldError,并将其注册到传入 validate 方法的 Errors 对象中。
配置自定义约束
每个 bean 验证约束由两部分组成:
-
一个
@Constraint注解,用于声明约束及其可配置的属性。 -
对实现
jakarta.validation.ConstraintValidator接口的实现,该实现实现了约束的行为。
要将声明与实现相关联,每个 @Constraint 注解都引用一个相应的 ConstraintValidator 实现类。在运行时,当在您的领域模型中遇到约束注解时,ConstraintValidatorFactory 会实例化所引用的实现类。
默认情况下,LocalValidatorFactoryBean 配置了一个使用 Spring 创建 SpringConstraintValidatorFactory 实例的 ConstraintValidator。这使得您的自定义 ConstraintValidators 能够像其他 Spring bean 一样受益于依赖注入。
以下示例显示了一个自定义 @Constraint 声明,后跟一个使用 Spring 进行依赖注入的关联 ConstraintValidator 实现:
-
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 实现可以将其依赖项
@Autowired 作为任何其他 Spring Bean。
基于Spring的方法验证
您可以通过定义一个 MethodValidationPostProcessor bean,将 Bean Validation 的方法校验功能集成到 Spring 上下文中:
-
Java
-
XML
import org.springframework.validation.beanvalidation.MethodValidationPostProcessor;
@Configuration
public class AppConfig {
@Bean
public static MethodValidationPostProcessor validationPostProcessor() {
return new MethodValidationPostProcessor();
}
}
<bean class="org.springframework.validation.beanvalidation.MethodValidationPostProcessor"/>
为了符合由 Spring 驱动的方法验证条件,目标类需要使用 Spring 的 @Validated 注解进行标注,该注解还可以选择性地声明要使用的验证组。有关 Hibernate Validator 和 Bean Validation 提供程序的设置详情,请参见
MethodValidationPostProcessor。
Spring MVC 和 WebFlux 对相同的底层方法验证提供了内置支持,但无需使用 AOP。因此,请务必查阅本节其余内容,以及 Spring MVC 的 验证 和 错误响应 部分,以及 WebFlux 的 验证 和 错误响应 部分。
方法验证异常
默认情况下,jakarta.validation.ConstraintViolationException 会与 ConstraintViolation 返回的一组 jakarta.validation.Validator 一起引发。作为替代方案,您可以改为引发 MethodValidationException,并将 ConstraintViolation 调整为 MessageSourceResolvable 错误。要启用,请设置以下标志:
-
Java
-
XML
import org.springframework.validation.beanvalidation.MethodValidationPostProcessor;
@Configuration
public class AppConfig {
@Bean
public static 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 列表,每个 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) {
// ...
}
}
ConstraintViolation 上的 Person.name() 通过以下方式适配到 FieldError:
-
错误代码
"Size.person.name"、"Size.name"、"Size.java.lang.String"和"Size" -
消息参数
"name",10和1(字段名及约束属性) -
默认消息“大小必须在1到10之间”
要自定义默认消息,您可以使用上述任一错误代码和消息参数,向
MessageSource
资源包添加属性。还请注意,消息参数 "name" 本身是一个带有错误代码
"person.name" 和 "name" 的 MessagreSourceResolvable,也可以进行自定义。例如:
- 属性
-
Size.person.name=Please, provide a {0} that is between {2} and {1} characters long person.name=username
ConstraintViolation 方法参数中的 degrees 会被适配为 MessageSourceResolvable,具体如下:
-
错误代码
"Max.myService#addStudent.degrees"、"Max.degrees"、"Max.int"、"Max" -
消息参数 "degrees2 和 2(字段名称和约束属性)
-
默认消息“必须小于或等于 2”
要自定义上述默认消息,您可以添加如下属性:
- 属性
-
Max.degrees=You cannot provide more than {1} {0}
其他配置选项
默认的 LocalValidatorFactoryBean 配置对于大多数情况来说已经足够。有许多配置选项可用于各种 Bean Validation 构造,从消息插值到遍历解析。有关这些选项的更多信息,请参阅
LocalValidatorFactoryBean
javadoc。
配置一个 DataBinder
您可以使用Validator配置DataBinder实例。配置完成后,可通过调用binder.validate()来触发Validator功能。
所有验证Errors将自动添加到绑定器的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和Validator配置多个dataBinder.addValidators实例。这在将全局配置的bean验证与Spring dataBinder.replaceValidators在DataBinder实例上本地配置相结合时非常有用。参见
Spring MVC验证配置。
Spring MVC 3 验证
查看 验证 部分在Spring MVC章节中。