Java Bean验证
Spring框架提供了对Java Bean验证API的支持。
Bean验证概述
Bean验证通过约束声明和元数据为Java应用程序提供了一种通用的验证方式。要使用它,您可以使用声明性验证约束对领域模型属性进行注释,然后由运行时强制执行这些约束。有内置的约束,您也可以定义自己的自定义约束。
考虑以下示例,显示了一个简单的PersonForm
模型,具有两个属性:
-
Java
-
Kotlin
public class PersonForm {
private String name;
private int age;
}
class PersonForm(
private val name: String,
private val age: Int
)
Bean验证允许您声明约束,如以下示例所示:
-
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验证验证器根据声明的约束验证此类的实例。有关API的一般信息,请参阅Bean验证。有关特定约束的信息,请参阅Hibernate验证器文档。要了解如何将Bean验证提供程序设置为Spring bean,请继续阅读。
配置Bean验证提供程序
Spring完全支持Bean验证API,包括将Bean验证提供程序引导为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验证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
声明,后跟一个使用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
实现可以像任何其他Spring bean一样具有其依赖项@Autowired
。
Spring驱动的方法验证
您可以通过MethodValidationPostProcessor
bean定义将Bean Validation的方法验证功能集成到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"/>
@Validated
注解进行注释,该注解还可以选择性地声明要使用的验证组。有关使用Hibernate Validator和Bean Validation提供程序设置详细信息,请参阅
MethodValidationPostProcessor
。
Spring MVC和WebFlux内置支持相同的底层方法验证,但无需使用AOP。因此,请查看本节的其余部分,还请参阅Spring MVC的验证和错误响应部分,以及WebFlux的验证和错误响应部分。
方法验证异常
默认情况下,会引发jakarta.validation.ConstraintViolationException
,其中包含jakarata.validation.Validator
返回的ConstraintViolation
集合。作为替代方案,您可以选择引发MethodValidationException
,其中包含适应为MessageSourceResolvable
错误的ConstraintViolation
。要启用,请设置以下标志:
-
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
是实现了org.springframework.validation.Errors
并将验证错误公开为FieldError
的ParameterErrors
。
自定义验证错误
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
(字段名称和约束属性)
MessageSource资源包添加任何上述错误代码和消息参数。还请注意,消息参数
"name"
本身是一个
MessagreSourceResolvable
,具有错误代码
"student.name"
和
"name"
,也可以进行自定义。例如:
- 属性
-
Size.student.name=请提供一个长度在{2}和{1}之间的{name} student.name=username
degrees
方法参数上的ConstraintViolation
会被适应为具有以下内容的MessageSourceResolvable
:
"Max.myService#addStudent.degrees"
、"Max.degrees"
、"Max.int"
和"Max"
- 属性
-
Max.degrees=您不能提供超过{1} {0}
其他配置选项
LocalValidatorFactoryBean
配置就足够了。有许多配置选项适用于各种Bean Validation构造,从消息插值到遍历解析。有关这些选项的更多信息,请参阅
LocalValidatorFactoryBean
的javadoc。
配置DataBinder
您可以使用Validator配置DataBinder实例。配置完成后,可以通过调用binder.validate()来调用Validator。任何验证错误都会自动添加到binder的BindingResult中。
以下示例展示了如何在绑定到目标对象后,以编程方式使用DataBinder调用验证逻辑:
-
Java
-
Kotlin
Foo target = new Foo();
DataBinder binder = new DataBinder(target);
binder.setValidator(new FooValidator());
// 绑定到目标对象
binder.bind(propertyValues);
// 验证目标对象
binder.validate();
// 获取包含任何验证错误的BindingResult
BindingResult results = binder.getBindingResult();
val target = Foo()
val binder = DataBinder(target)
binder.validator = FooValidator()
// 绑定到目标对象
binder.bind(propertyValues)
// 验证目标对象
binder.validate()
// 获取包含任何验证错误的BindingResult
val results = binder.bindingResult
您还可以通过dataBinder.addValidators和dataBinder.replaceValidators为DataBinder配置多个Validator实例。当将全局配置的bean验证与在DataBinder实例上本地配置的Spring Validator结合使用时,这将非常有用。请参阅Spring MVC验证配置。
Spring MVC 3验证
请参阅Spring MVC章节中的验证。