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

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 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,然後將 ConstraintViolations 適配到 FieldErrors,並將它們註冊到傳遞到 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

方法驗證依賴於目標類別周圍的 AOP 代理,介面上方法的 JDK 動態代理或 CGLIB 代理。代理的使用存在某些限制,其中一些限制在 了解 AOP 代理 中有所描述。此外,請記住始終在代理類別上使用方法和存取器;直接欄位存取將無法運作。

Spring MVC 和 WebFlux 具有對相同底層方法驗證的內建支援,但不需要 AOP。因此,請查看本節的其餘部分,並參閱 Spring MVC 驗證錯誤回應 章節,以及 WebFlux 驗證錯誤回應 章節。

方法驗證例外

預設情況下,會引發 jakarta.validation.ConstraintViolationException,其中包含 jakarta.validation.Validator 傳回的 ConstraintViolations 集合。作為替代方案,您可以改為引發 MethodValidationException,其中 ConstraintViolations 適配到 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 包含 ParameterValidationResults 的列表,這些列表按方法參數對錯誤進行分組,並且每個列表都公開一個 MethodParameter、引數值以及從 ConstraintViolations 適配的 MessageSourceResolvable 錯誤列表。對於具有欄位和屬性上級聯違規的 @Valid 方法參數,ParameterValidationResultParameterErrors,它實作 org.springframework.validation.Errors 並將驗證錯誤公開為 FieldErrors。

自訂驗證錯誤

適配的 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"101(欄位名稱和約束屬性)

  • 預設訊息「大小必須介於 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.addValidatorsdataBinder.replaceValidators 使用多個 Validator 實例來設定 DataBinder。當將全域設定的 Bean 驗證與在 DataBinder 實例上本機設定的 Spring Validator 結合使用時,這非常有用。請參閱 Spring MVC 驗證設定

Spring MVC 3 驗證

請參閱 Spring MVC 章節中的 驗證