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.ValidatorFactoryjakarta.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.ValidatorFactoryjakarta.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

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

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并将验证错误公开为FieldErrorParameterErrors

自定义验证错误

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"101(字段名称和约束属性)

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章节中的验证