Java Bean 驗證
Spring Framework 提供 Java Bean Validation 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 Validation。有關特定約束,請參閱 Hibernate Validator 文件。若要了解如何設定 Bean 驗證提供者作為 Spring Bean,請繼續閱讀。
設定 Bean 驗證提供者
Spring 為 Bean 驗證 API 提供完整支援,包括啟動 Bean 驗證提供者作為 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 驗證提供者(例如 Hibernate Validator)預期存在於類別路徑中,並會自動偵測到。
注入 Jakarta Validator
LocalValidatorFactoryBean
同時實作 jakarta.validation.ValidatorFactory
和 jakarta.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 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
s 適配到 FieldError
s,並將它們註冊到傳遞到 validate
方法的 Errors
物件。
設定自訂約束
每個 Bean 驗證約束都包含兩個部分
-
一個
@Constraint
註解,宣告約束及其可設定的屬性。 -
jakarta.validation.ConstraintValidator
介面的實作,用於實作約束的行為。
為了將宣告與實作關聯起來,每個 @Constraint
註解都會參考對應的 ConstraintValidator
實作類別。在執行階段,當在您的網域模型中遇到約束註解時,ConstraintValidatorFactory
會實例化參考的實作。
預設情況下,LocalValidatorFactoryBean
會設定 SpringConstraintValidatorFactory
,後者使用 Spring 來建立 ConstraintValidator
實例。這可讓您的自訂 ConstraintValidators
受益於相依性注入,就像任何其他 Spring Bean 一樣。
以下範例顯示了自訂 @Constraint
宣告,後跟相關聯的 ConstraintValidator
實作,後者使用 Spring 進行相依性注入
-
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 驗證的方法驗證功能整合到 Spring Context 中
-
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 驗證提供者的設定詳細資訊,請參閱 MethodValidationPostProcessor
。
Spring MVC 和 WebFlux 具有對相同底層方法驗證的內建支援,但不需要 AOP。因此,請查看本節的其餘部分,並參閱 Spring MVC 驗證 和 錯誤回應 章節,以及 WebFlux 驗證 和 錯誤回應 章節。
方法驗證例外
預設情況下,會引發 jakarta.validation.ConstraintViolationException
,其中包含 jakarta.validation.Validator
傳回的 ConstraintViolation
s 集合。作為替代方案,您可以改為引發 MethodValidationException
,其中 ConstraintViolation
s 適配到 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
s 的列表,這些列表按方法參數對錯誤進行分組,並且每個列表都公開一個 MethodParameter
、引數值以及從 ConstraintViolation
s 適配的 MessageSourceResolvable
錯誤列表。對於具有欄位和屬性上級聯違規的 @Valid
方法參數,ParameterValidationResult
是 ParameterErrors
,它實作 org.springframework.validation.Errors
並將驗證錯誤公開為 FieldError
s。
自訂驗證錯誤
適配的 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"
-
訊息引數 "degrees2 和 2(欄位名稱和約束屬性)
-
預設訊息「必須小於或等於 2」
若要自訂上述預設訊息,您可以新增一個屬性,例如
- 屬性
-
Max.degrees=You cannot provide more than {1} {0}
其他設定選項
預設的 LocalValidatorFactoryBean
設定足以應付大多數情況。對於各種 Bean 驗證建構,從訊息內插到遍歷解析,都有許多設定選項。有關這些選項的更多資訊,請參閱 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
實例來設定 DataBinder
。當將全域設定的 Bean 驗證與在 DataBinder 實例上本機設定的 Spring Validator
結合使用時,這非常有用。請參閱 Spring MVC 驗證設定。
Spring MVC 3 驗證
請參閱 Spring MVC 章節中的 驗證。