对于最新稳定版本,请使用 Spring Framework 7.0.6spring-doc.cadn.net.cn

Java Bean 验证

Spring 框架提供了对 Java Bean Validation API 的支持。spring-doc.cadn.net.cn

Bean 验证概述

Bean Validation 为 Java 应用程序提供了一种通过约束声明和元数据进行验证的通用方式。要使用它,您可以在领域模型属性上添加声明式验证约束注解,这些约束将在运行时被强制执行。该框架提供了内置的约束,您也可以定义自己的自定义约束。spring-doc.cadn.net.cn

考虑以下示例,它展示了一个包含两个属性的简单 PersonForm 模型:spring-doc.cadn.net.cn

public class PersonForm {
	private String name;
	private int age;
}
class PersonForm(
		private val name: String,
		private val age: Int
)

Bean Validation 允许你声明如下示例所示的约束:spring-doc.cadn.net.cn

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 Validation 提供程序配置为 Spring Bean,请继续阅读。spring-doc.cadn.net.cn

配置 Bean 验证提供程序

Spring 对 Bean Validation API 提供全面支持,包括将 Bean Validation 提供程序作为 Spring bean 进行引导。这使得你可以在应用程序中任何需要验证的地方注入 jakarta.validation.ValidatorFactoryjakarta.validation.Validatorspring-doc.cadn.net.cn

您可以使用 LocalValidatorFactoryBean 将默认的 Validator 配置为 Spring bean,如下例所示:spring-doc.cadn.net.cn

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 Validation,使用其默认的引导机制进行初始化。系统期望在类路径中存在一个 Bean Validation 提供者(例如 Hibernate Validator),并会自动检测到它。spring-doc.cadn.net.cn

注入 Jakarta Validator

LocalValidatorFactoryBean 同时实现了 jakarta.validation.ValidatorFactoryjakarta.validation.Validator,因此如果你更倾向于直接使用 Bean Validation API 来应用验证逻辑,可以注入后者的引用,如下例所示:spring-doc.cadn.net.cn

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,你可以注入后者的引用。spring-doc.cadn.net.cn

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, 并将其注册到传入 Errors 方法的 validate 对象中。spring-doc.cadn.net.cn

配置自定义约束

每个 Bean Validation 约束由两部分组成:spring-doc.cadn.net.cn

为了将声明与实现关联起来,每个 @Constraint 注解都会引用一个对应的 ConstraintValidator 实现类。在运行时,当在您的领域模型中遇到约束注解时,ConstraintValidatorFactory 会实例化所引用的实现类。spring-doc.cadn.net.cn

默认情况下,LocalValidatorFactoryBean 会配置一个 SpringConstraintValidatorFactory, 该工厂使用 Spring 来创建 ConstraintValidator 实例。这使得您自定义的 ConstraintValidators 能够像其他 Spring Bean 一样受益于依赖注入。spring-doc.cadn.net.cn

以下示例展示了一个自定义的 @Constraint 声明,以及一个关联的 ConstraintValidator 实现,该实现使用 Spring 进行依赖注入:spring-doc.cadn.net.cn

@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
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-doc.cadn.net.cn

Spring 驱动的方法验证

你可以通过定义一个 MethodValidationPostProcessor Bean,将 Bean Validation 的方法验证功能集成到 Spring 上下文中:spring-doc.cadn.net.cn

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 注解进行标注,该注解还可以可选地声明要使用的验证组。请参阅 MethodValidationPostProcessor 以获取有关使用 Hibernate Validator 和 Bean Validation 提供程序的设置详情。spring-doc.cadn.net.cn

方法验证依赖于目标类周围的AOP 代理,这些代理可以是针对接口方法的 JDK 动态代理,也可以是 CGLIB 代理。 使用代理存在某些限制,其中一些在理解 AOP 代理中有详细说明。此外,请记住始终通过代理类的方法和访问器进行操作;直接访问字段将无法正常工作。spring-doc.cadn.net.cn

Spring MVC 和 WebFlux 对底层方法验证提供了内置支持,且无需使用 AOP。因此,请务必阅读本节的其余内容,并参阅 Spring MVC 的 验证错误响应 章节,以及 WebFlux 的 验证错误响应 章节。spring-doc.cadn.net.cn

方法验证异常

默认情况下,会抛出 jakarta.validation.ConstraintViolationException 异常,并附带由 ConstraintViolation 返回的一组 jakarta.validation.Validator。作为替代方案,你也可以改为抛出 MethodValidationException,并将 ConstraintViolation 转换为符合 MessageSourceResolvable 格式的错误。要启用此功能,请设置以下标志:spring-doc.cadn.net.cn

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 方法参数,其 ParameterValidationResultParameterErrors,它实现了 org.springframework.validation.Errors 并将验证错误暴露为 FieldErrorspring-doc.cadn.net.cn

自定义验证错误

适配后的 MessageSourceResolvable 错误可以通过配置的 MessageSource 转换为错误消息,结合区域设置(locale)和语言特定的资源包(resource bundles)向用户显示。本节提供一个示例以作说明。spring-doc.cadn.net.cn

给定以下类声明:spring-doc.cadn.net.cn

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) {
		// ...
	}
}

ConstraintViolationPerson.name() 将被转换为如下所示的 FieldErrorspring-doc.cadn.net.cn

要自定义默认消息,您可以使用上述任意错误代码和消息参数,向MessageSource资源包中添加属性。另请注意,消息参数"name"本身也是一个MessagreSourceResolvable,其错误代码为"person.name""name",也可以进行自定义。例如:spring-doc.cadn.net.cn

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

ConstraintViolation 方法参数上的 degrees 将被转换为一个 MessageSourceResolvable,其内容如下:spring-doc.cadn.net.cn

要自定义上述默认消息,您可以添加如下属性:spring-doc.cadn.net.cn

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

其他配置选项

默认的 LocalValidatorFactoryBean 配置足以满足大多数情况。针对各种 Bean Validation 构造,存在多种配置选项,范围涵盖消息插值到遍历解析。有关这些选项的更多信息,请参阅 LocalValidatorFactoryBean 的 javadoc。spring-doc.cadn.net.cn

配置一个DataBinder

你可以使用一个 DataBinder 来配置 Validator 实例。配置完成后,可以通过调用 Validator 来触发验证器。所有验证产生的 binder.validate() 会自动添加到绑定器的 Errors 中。spring-doc.cadn.net.cn

以下示例展示了如何以编程方式使用 DataBinder,在绑定到目标对象后调用验证逻辑:spring-doc.cadn.net.cn

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

您还可以通过 DataBinderValidatordataBinder.addValidators 配置多个 dataBinder.replaceValidators 实例。当需要将全局配置的 Bean 验证与在 DataBinder 实例上本地配置的 Spring Validator 结合使用时,这种做法非常有用。请参阅 Spring MVC 验证配置spring-doc.cadn.net.cn

Spring MVC 3 验证

参见 Spring MVC 章节中的验证部分。spring-doc.cadn.net.cn