Java Bean 驗證
Spring Framework 為 Java Bean Validation API 提供支援。
Bean Validation 概述
Bean Validation 透過約束宣告和元資料為 Java 應用程式提供了一種通用的驗證方式。要使用它,您需要使用宣告式驗證約束來註解領域模型屬性,然後由執行時強制執行這些約束。有內建約束,您還可以定義自己的自定義約束。
請看以下示例,它顯示了一個帶有兩個屬性的簡單 PersonForm 模型
-
Java
-
Kotlin
public class PersonForm {
private String name;
private int age;
}
class PersonForm(
private val name: String,
private val age: Int
)
Bean Validation 允許您宣告約束,如以下示例所示
-
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 Validation 驗證器然後根據宣告的約束驗證此類的例項。有關 API 的一般資訊,請參閱 Bean Validation。有關特定約束的資訊,請參閱 Hibernate Validator 文件。要了解如何將 bean 驗證提供程式設定為 Spring bean,請繼續閱讀。
配置 Bean Validation 提供程式
Spring 提供對 Bean Validation API 的全面支援,包括將 Bean Validation 提供程式引導為 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 Validation 提供程式(例如 Hibernate Validator)存在於類路徑中並自動檢測到。
注入 Jakarta Validator
LocalValidatorFactoryBean 實現了 jakarta.validation.ValidatorFactory 和 jakarta.validation.Validator,因此如果您傾向於直接使用 Bean Validation 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 Validator
除了實現 jakarta.validation.Validator,LocalValidatorFactoryBean 還適應 org.springframework.validation.Validator,因此如果您的 bean 需要 Spring Validation 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,然後將 ConstraintViolation 轉換為 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
-
Kotlin
-
Xml
@Configuration
public class ApplicationConfiguration {
@Bean
public static MethodValidationPostProcessor validationPostProcessor() {
return new MethodValidationPostProcessor();
}
}
@Configuration
class ApplicationConfiguration {
companion object {
@Bean
@JvmStatic
fun validationPostProcessor() = MethodValidationPostProcessor()
}
}
<bean class="org.springframework.validation.beanvalidation.MethodValidationPostProcessor"/>
要符合 Spring 驅動的方法驗證條件,目標類需要使用 Spring 的 @Validated 註解進行註解,該註解還可以選擇宣告要使用的驗證組。有關 Hibernate Validator 和 Bean Validation 提供程式設定的詳細資訊,請參閱 MethodValidationPostProcessor。
Spring MVC 和 WebFlux 對相同的基礎方法驗證有內建支援,但不需要 AOP。因此,請檢查本節的其餘部分,並參閱 Spring MVC 驗證 和 錯誤響應 部分,以及 WebFlux 驗證 和 錯誤響應 部分。
方法驗證異常
預設情況下,會丟擲 jakarta.validation.ConstraintViolationException,其中包含 jakarta.validation.Validator 返回的 ConstraintViolation 集合。作為替代方案,您可以改為丟擲 MethodValidationException,其中 ConstraintViolation 轉換為 MessageSourceResolvable 錯誤。要啟用此功能,請設定以下標誌
-
Java
-
Kotlin
-
Xml
@Configuration
public class ApplicationConfiguration {
@Bean
public static MethodValidationPostProcessor validationPostProcessor() {
MethodValidationPostProcessor processor = new MethodValidationPostProcessor();
processor.setAdaptConstraintViolations(true);
return processor;
}
}
@Configuration
class ApplicationConfiguration {
companion object {
@Bean
@JvmStatic
fun validationPostProcessor() = MethodValidationPostProcessor().apply {
setAdaptConstraintViolations(true)
}
}
}
<bean class="org.springframework.validation.beanvalidation.MethodValidationPostProcessor">
<property name="adaptConstraintViolations" value="true"/>
</bean>
MethodValidationException 包含一個 ParameterValidationResult 列表,這些列表按方法引數分組錯誤,每個錯誤都暴露一個 MethodParameter、引數值和從 ConstraintViolation 轉換而來的 MessageSourceResolvable 錯誤列表。對於在欄位和屬性上具有級聯違規的 @Valid 方法引數,ParameterValidationResult 是 ParameterErrors,它實現了 org.springframework.validation.Errors 並將驗證錯誤暴露為 FieldError。
自定義驗證錯誤
轉換後的 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.person.name"、"Size.name"、"Size.java.lang.String"和"Size" -
訊息引數
"name"、10和1(欄位名和約束屬性) -
預設訊息 "大小必須在 1 到 10 之間"
要自定義預設訊息,您可以將屬性新增到 MessageSource 資源包中,使用上述任何錯誤程式碼和訊息引數。另請注意,訊息引數 "name" 本身是一個 MessageSourceResolvable,帶有錯誤程式碼 "person.name" 和 "name",也可以自定義。例如
- 屬性
-
Size.person.name=Please, provide a {0} that is between {2} and {1} characters long person.name=username
對 degrees 方法引數的 ConstraintViolation 轉換為具有以下內容的 MessageSourceResolvable
-
錯誤程式碼
"Max.myService#addStudent.degrees"、"Max.degrees"、"Max.int"、"Max" -
訊息引數 "degrees" 和 2(欄位名和約束屬性)
-
預設訊息 "必須小於或等於 2"
要自定義上述預設訊息,您可以新增一個屬性,例如
- 屬性
-
Max.degrees=You cannot provide more than {1} {0}
其他配置選項
預設的 LocalValidatorFactoryBean 配置適用於大多數情況。有許多配置選項適用於各種 Bean Validation 構造,從訊息插值到遍歷解析。有關這些選項的更多資訊,請參閱 LocalValidatorFactoryBean javadoc。
配置 DataBinder
您可以使用 Validator 配置 DataBinder 例項。配置後,您可以透過呼叫 binder.validate() 來呼叫 Validator。任何驗證 Errors 都會自動新增到 binder 的 BindingResult 中。
以下示例顯示瞭如何以程式設計方式使用 DataBinder 在繫結到目標物件後呼叫驗證邏輯
-
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
您還可以透過 dataBinder.addValidators 和 dataBinder.replaceValidators 配置多個 Validator 例項。這在將全域性配置的 bean 驗證與在 DataBinder 例項上本地配置的 Spring Validator 結合使用時很有用。請參閱 Spring MVC 驗證配置。
Spring MVC 驗證
請參閱 Spring MVC 章中的 驗證。