|
对于最新的稳定版本,请使用 Spring Framework 6.2.4! |
Java Bean 验证
Spring Framework 提供了对 Java Bean 验证 API 的支持。
Bean 验证概述
Bean Validation 提供了一种常见的验证方法,通过 constraint declaration 和 元数据。要使用它,您可以使用 声明性验证约束,然后由运行时强制执行。有 built-in constraints,您还可以定义自己的自定义 constraints。
请考虑以下示例,它显示了一个简单的PersonFormmodel 具有两个属性:
-
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 的 API 创建。请参阅 Hibernate Validator 文档 特定约束。了解如何将 Bean 验证提供程序设置为 Spring Bean,请继续阅读。
配置 Bean 验证提供程序
Spring 提供了对 Bean 验证 API 的全面支持,包括
Bean Validation 提供程序作为 Spring Bean。这样,您就可以注入jakarta.validation.ValidatorFactory或jakarta.validation.ValidatorWhere 验证
在您的应用程序中需要。
您可以使用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"/>
The basic configuration in the preceding example triggers bean validation to initialize by
using its default bootstrap mechanism. A Bean Validation provider, such as the Hibernate
Validator, is expected to be present in the classpath and is automatically detected.
Inject Jakarta Validator
LocalValidatorFactoryBean implements both jakarta.validation.ValidatorFactory and
jakarta.validation.Validator, so you can inject a reference to the latter to
apply validation logic if you prefer to work with the Bean Validation API directly,
as the following example shows:
-
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)
Inject Spring Validator
In addition to implementing jakarta.validation.Validator, LocalValidatorFactoryBean
also adapts to org.springframework.validation.Validator, so you can inject a reference
to the latter if your bean requires the Spring Validation API.
For example:
-
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)
When used as org.springframework.validation.Validator, LocalValidatorFactoryBean
invokes the underlying jakarta.validation.Validator, and then adapts
ConstraintViolations to FieldErrors, and registers them with the Errors object
passed into the validate method.
Configure Custom Constraints
Each bean validation constraint consists of two parts:
-
A @Constraint annotation that declares the constraint and its configurable properties.
-
An implementation of the jakarta.validation.ConstraintValidator interface that implements
the constraint’s behavior.
To associate a declaration with an implementation, each @Constraint annotation
references a corresponding ConstraintValidator implementation class. At runtime, a
ConstraintValidatorFactory instantiates the referenced implementation when the
constraint annotation is encountered in your domain model.
By default, the LocalValidatorFactoryBean configures a SpringConstraintValidatorFactory
that uses Spring to create ConstraintValidator instances. This lets your custom
ConstraintValidators benefit from dependency injection like any other Spring bean.
The following example shows a custom @Constraint declaration followed by an associated
ConstraintValidator implementation that uses Spring for dependency injection:
-
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 {
// ...
}
As the preceding example shows, a ConstraintValidator implementation can have its dependencies
@Autowired as any other Spring bean.
Spring-driven Method Validation
You can integrate the method validation feature of Bean Validation into a
Spring context through a MethodValidationPostProcessor bean definition:
-
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"/>
To be eligible for Spring-driven method validation, target classes need to be annotated
with Spring’s @Validated annotation, which can optionally also declare the validation
groups to use. See
MethodValidationPostProcessor
for setup details with the Hibernate Validator and Bean Validation providers.
Method validation relies on AOP Proxies around the
target classes, either JDK dynamic proxies for methods on interfaces or CGLIB proxies.
There are certain limitations with the use of proxies, some of which are described in
Understanding AOP Proxies. In addition remember
to always use methods and accessors on proxied classes; direct field access will not work.
Spring MVC and WebFlux have built-in support for the same underlying method validation but without
the need for AOP. Therefore, do check the rest of this section, and also see the Spring MVC
Validation and
Error Responses sections, and the WebFlux
Validation and
Error Responses sections.
Method Validation Exceptions
By default, jakarta.validation.ConstraintViolationException is raised with the set of
ConstraintViolations returned by jakarta.validation.Validator. As an alternative,
you can have MethodValidationException raised instead with ConstraintViolations
adapted to MessageSourceResolvable errors. To enable set the following flag:
-
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 contains a list of ParameterValidationResults which
group errors by method parameter, and each exposes a MethodParameter, the argument
value, and a list of MessageSourceResolvable errors adapted from
ConstraintViolations. For @Valid method parameters with cascaded violations on
fields and properties, the ParameterValidationResult is ParameterErrors which
implements org.springframework.validation.Errors and exposes validation errors as
FieldErrors.
Customizing Validation Errors
The adapted MessageSourceResolvable errors can be turned into error messages to
display to users through the configured MessageSource with locale and language specific
resource bundles. This section provides an example for illustration.
Given the following class declarations:
-
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) {
// ...
}
}
A ConstraintViolation on Person.name() is adapted to a FieldError with the following:
-
Error codes "Size.person.name", "Size.name", "Size.java.lang.String", and "Size"
-
Message arguments "name", 10, and 1 (the field name and the constraint attributes)
-
Default message "size must be between 1 and 10"
To customize the default message, you can add properties to
MessageSource
resource bundles using any of the above errors codes and message arguments. Note also that the
message argument "name" is itself a MessagreSourceResolvable with error codes
"person.name" and "name" and can customized too. For example:
- Properties
-
Size.person.name=Please, provide a {0} that is between {2} and {1} characters long
person.name=username
A ConstraintViolation on the degrees method parameter is adapted to a
MessageSourceResolvable with the following:
-
Error codes "Max.myService#addStudent.degrees", "Max.degrees", "Max.int", "Max"
-
Message arguments "degrees2 and 2 (the field name and the constraint attribute)
-
Default message "must be less than or equal to 2"
To customize the above default message, you can add a property such as:
- Properties
-
Max.degrees=You cannot provide more than {1} {0}
Additional Configuration Options
The default LocalValidatorFactoryBean configuration suffices for most
cases. There are a number of configuration options for various Bean Validation
constructs, from message interpolation to traversal resolution. See the
LocalValidatorFactoryBean
javadoc for more information on these options.
Configuring a DataBinder
You can configure a DataBinder instance with a Validator. Once configured, you can
invoke the Validator by calling binder.validate(). Any validation Errors are
automatically added to the binder’s BindingResult.
The following example shows how to use a DataBinder programmatically to invoke validation
logic after binding to a target object:
-
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
You can also configure a DataBinder with multiple Validator instances through
dataBinder.addValidators and dataBinder.replaceValidators. This is useful when
combining globally configured bean validation with a Spring Validator configured
locally on a DataBinder instance. See
Spring MVC Validation Configuration.
Spring MVC 3 Validation
See Validation in the Spring MVC chapter.