跳到主要内容

Java Bean 验证

ChatGPT-4o 中英对照 Java Bean Validation

Spring 框架为 Java Bean Validation API 提供支持。

Bean Validation 概述

Bean Validation 为 Java 应用程序提供了一种通过约束声明和元数据进行验证的通用方法。要使用它,您可以在领域模型属性上使用声明性验证约束进行注释,这些约束随后由运行时强制执行。它提供了内置的约束,您也可以定义自己的自定义约束。

考虑以下示例,其中显示了一个简单的 PersonForm 模型,具有两个属性:

public class PersonForm {
private String name;
private int age;
}
java

Bean Validation 允许你声明约束条件,如以下示例所示:

public class PersonForm {

@NotNull
@Size(max=64)
private String name;

@Min(0)
private int age;
}
java

然后,Bean Validation 验证器根据声明的约束验证此类的实例。有关 API 的一般信息,请参阅 Bean Validation。有关特定约束,请参阅 Hibernate Validator 文档。要了解如何将 bean 验证提供程序设置为 Spring bean,请继续阅读。

配置 Bean Validation 提供者

Spring 完全支持 Bean Validation API,包括将 Bean Validation 提供者引导为 Spring bean。这使您可以在应用程序中需要验证的任何地方注入 jakarta.validation.ValidatorFactoryjakarta.validation.Validator

您可以使用 LocalValidatorFactoryBean 将默认的 Validator 配置为 Spring bean,如下例所示:

import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;

@Configuration
public class AppConfig {

@Bean
public LocalValidatorFactoryBean validator() {
return new LocalValidatorFactoryBean();
}
}
java

前面的示例中的基本配置通过使用其默认的引导机制触发 bean 验证的初始化。像 Hibernate Validator 这样的 Bean 验证提供者应该存在于类路径中,并会被自动检测到。

注入 Jakarta Validator

LocalValidatorFactoryBean 实现了 jakarta.validation.ValidatorFactoryjakarta.validation.Validator,因此如果你更喜欢直接使用 Bean Validation API,可以注入后者的引用来应用验证逻辑,如下例所示:

import jakarta.validation.Validator;

@Service
public class MyService {

@Autowired
private Validator validator;
}
java

注入 Spring Validator

除了实现 jakarta.validation.Validator 之外,LocalValidatorFactoryBean 还适配 org.springframework.validation.Validator,因此如果你的 bean 需要 Spring Validation API,你可以注入后者的引用。

例如:

import org.springframework.validation.Validator;

@Service
public class MyService {

@Autowired
private Validator validator;
}
java

当用作 org.springframework.validation.Validator 时,LocalValidatorFactoryBean 调用底层的 jakarta.validation.Validator,然后将 ConstraintViolation 适配为 FieldError,并将它们注册到传递给 validate 方法的 Errors 对象中。

配置自定义约束

每个 bean 验证约束由两部分组成:

  • 一个 @Constraint 注解,用于声明约束及其可配置属性。

  • 一个实现 jakarta.validation.ConstraintValidator 接口的类,用于实现约束的行为。

要将声明与实现关联,每个 @Constraint 注解都会引用一个对应的 ConstraintValidator 实现类。在运行时,当在你的领域模型中遇到约束注解时,ConstraintValidatorFactory 会实例化所引用的实现。

默认情况下,LocalValidatorFactoryBean 配置了一个 SpringConstraintValidatorFactory,该工厂使用 Spring 来创建 ConstraintValidator 实例。这使得自定义的 ConstraintValidators 可以像其他 Spring bean 一样受益于依赖注入。

以下示例展示了一个自定义 @Constraint 声明,后面是一个使用 Spring 进行依赖注入的关联 ConstraintValidator 实现:

@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy=MyConstraintValidator.class)
public @interface MyConstraint {
}
java
import jakarta.validation.ConstraintValidator;

public class MyConstraintValidator implements ConstraintValidator {

@Autowired;
private Foo aDependency;

// ...
}
java

如前面的示例所示,ConstraintValidator 实现可以像其他任何 Spring bean 一样通过 @Autowired 注入其依赖项。

基于 Spring 的方法验证

您可以通过 MethodValidationPostProcessor bean 定义将 Bean Validation 的方法验证功能集成到 Spring 上下文中:

@Configuration
public class ApplicationConfiguration {

@Bean
public static MethodValidationPostProcessor validationPostProcessor() {
return new MethodValidationPostProcessor();
}
}
java

要符合 Spring 驱动的方法验证的条件,目标类需要用 Spring 的 @Validated 注解进行标注,该注解还可以选择性地声明要使用的验证组。有关使用 Hibernate Validator 和 Bean Validation 提供程序的设置详细信息,请参见 MethodValidationPostProcessor

提示

方法验证依赖于目标类周围的 AOP 代理,可以是接口方法的 JDK 动态代理或 CGLIB 代理。使用代理存在某些限制,其中一些在理解 AOP 代理中有所描述。此外,请记住始终使用代理类上的方法和访问器;直接访问字段将不起作用。

Spring MVC 和 WebFlux 对相同的底层方法验证提供了内置支持,但不需要 AOP。因此,请查看本节的其余部分,还可以参见 Spring MVC 的验证错误响应部分,以及 WebFlux 的验证错误响应部分。

方法验证异常

默认情况下,jakarta.validation.ConstraintViolationException 会与由 jakarta.validation.Validator 返回的 ConstraintViolation 集一起抛出。作为替代方案,您可以选择抛出 MethodValidationException,其中 ConstraintViolation 被适配为 MessageSourceResolvable 错误。要启用此功能,请设置以下标志:

@Configuration
public class ApplicationConfiguration {

@Bean
public static MethodValidationPostProcessor validationPostProcessor() {
MethodValidationPostProcessor processor = new MethodValidationPostProcessor();
processor.setAdaptConstraintViolations(true);
return processor;
}
}
java

MethodValidationException 包含一个 ParameterValidationResult 列表,该列表按方法参数对错误进行分组,并且每个都公开一个 MethodParameter、参数值以及从 ConstraintViolation 适配而来的 MessageSourceResolvable 错误列表。对于具有字段和属性级联违规的 @Valid 方法参数,ParameterValidationResultParameterErrors,它实现了 org.springframework.validation.Errors 并将验证错误公开为 FieldError

自定义验证错误

经过调整的 MessageSourceResolvable 错误可以通过配置的 MessageSource 和特定语言环境的资源包转换为错误消息,以显示给用户。本节提供了一个示例进行说明。

给定以下类声明:

record Person(@Size(min = 1, max = 10) String name) {
}

@Validated
public class MyService {

void addStudent(@Valid Person person, @Max(2) int degrees) {
// ...
}
}
java

Person.name() 上的 ConstraintViolation 被转换为 FieldError,如下所示:

  • 错误代码 "Size.person.name""Size.name""Size.java.lang.String""Size"

  • 消息参数 "name"101(字段名称和约束属性)

  • 默认消息 "size must be between 1 and 10"

要自定义默认消息,您可以使用上述任何错误代码和消息参数向 MessageSource 资源包添加属性。还要注意,消息参数 "name" 本身是一个具有错误代码 "person.name""name"MessageSourceResolvable,也可以进行自定义。例如:

属性

Size.person.name=Please, provide a {0} that is between {2} and {1} characters long
person.name=username
properties

degrees 方法参数上的 ConstraintViolation 被转换为 MessageSourceResolvable,如下所示:

  • 错误代码 "Max.myService#addStudent.degrees""Max.degrees""Max.int""Max"

  • 消息参数 "degrees2 和 2(字段名称和约束属性)

  • 默认消息 "必须小于或等于 2"

要自定义上述默认消息,您可以添加一个属性,例如:

属性

Max.degrees=You cannot provide more than {1} {0}
properties

其他配置选项

默认的 LocalValidatorFactoryBean 配置适用于大多数情况。对于各种 Bean Validation 构造,有许多配置选项,从消息插值到遍历解析。有关这些选项的更多信息,请参阅 LocalValidatorFactoryBean javadoc。

配置 DataBinder

您可以使用 Validator 配置一个 DataBinder 实例。配置完成后,您可以通过调用 binder.validate() 来调用 Validator。任何验证 Errors 都会自动添加到绑定器的 BindingResult 中。

下面的示例展示了如何以编程方式使用 DataBinder 在绑定到目标对象后调用验证逻辑:

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();
java

您还可以通过 dataBinder.addValidatorsdataBinder.replaceValidators 配置具有多个 Validator 实例的 DataBinder。当将全局配置的 bean 验证与在 DataBinder 实例上本地配置的 Spring Validator 结合使用时,这非常有用。请参阅 Spring MVC 验证配置

Spring MVC 3 验证

请参阅 Spring MVC 章节中的验证