Spring 有一个 Validator 接口,你可以用它来验证对象。验证器接口通过使用一个 Errors 对象来工作,这样,在验证时,验证器可以向 Errors 对象报告验证失败。
请考虑下面这个小数据对象的例子:
public class Person {
private String name;
private int age;
// 省略了 getters 和 setters 方法
}
下一个例子通过实现 org.springframework.validation.Validator
接口的以下两个方法为 Person 类提供验证行为:
supports(Class)
:这个验证器可以验证提供的类的实例吗?validate(Object, org.springframework.validation.Errors)
:验证给定的对象,如果出现验证错误,则用给定的 Errors 对象登记这些错误。.
实现一个验证器是相当简单的,特别是当你知道 Spring 框架也提供的 ValidationUtils 帮助类时。下面的例子为 Person 实例实现了 Validator。
package cn.mrcode.study.springdocsread.data;
import org.springframework.validation.Errors;
import org.springframework.validation.ValidationUtils;
import org.springframework.validation.Validator;
/**
* @author mrcode
* @date 2022/3/1 13:26
*/
public class PersonValidator implements Validator {
/**
* 这个验证器只验证 Person 的实例
*
* @param clazz
* @return
*/
@Override
public boolean supports(Class<?> clazz) {
return Person.class.equals(clazz);
}
@Override
public void validate(Object obj, Errors e) {
// 如果对象中的 name 字段为 空字符串或则 null ,则将给定的错误信息(errorCode)存放在 errors 中
ValidationUtils.rejectIfEmpty(e, "name", "name.empty");
Person p = (Person) obj;
if (p.getAge() < 0) {
// 添加错误信息
e.rejectValue("age", "negativevalue");
} else if (p.getAge() > 110) {
e.rejectValue("age", "too.darn.old");
}
}
}
ValidationUtils 类的静态方法 rejectIfEmpty(..)
用于拒绝 name 属性,如果它是 null 或空字符串。看一下 ValidationUtils 的 javadoc,看看除了前面显示的例子外,它还提供了哪些功能。
下面是使用方式
package cn.mrcode.study.springdocsread.data;
import org.springframework.validation.FieldError;
import org.springframework.validation.MapBindingResult;
import java.util.HashMap;
import java.util.List;
/**
* @author mrcode
* @date 2022/3/1 13:38
*/
public class DemoTest {
public static void main(String[] args) {
final PersonValidator validator = new PersonValidator();
final MapBindingResult errors = new MapBindingResult(new HashMap<>(), "persion");
validator.validate(new Person(), errors);
final List<FieldError> fieldErrors = errors.getFieldErrors();
for (FieldError fieldError : fieldErrors) {
System.out.println("字段=" + fieldError.getField() + " ;错误码=" + fieldError.getCode());
System.out.println(fieldError);
}
}
}
输出信息如下
字段=name ;错误码=name.empty
// 这个里面的 codes 不仅仅是 name.empty,还有其他的,这个下一个章节会讲为什么会多出来两个 code
Field error in object 'persion' on field 'name': rejected value [null]; codes [name.empty.persion.name,name.empty.name,name.empty]; arguments []; default message [null]
当然,实现一个单一的 Validator 类来验证富对象中的每个嵌套对象是可能的,但最好是将每个嵌套类对象的验证逻辑封装在自己的 Validator 实现中。一个简单的 「丰富」对象的例子是一个 Customer ,它由两个字符串属性(一个名字和一个第二名字)和一个复杂的 Address 对象组成。Address 对象可以独立于客户对象使用,所以已经实现了一个独立的 AddressValidator。如果你想让你的 CustomerValidator 重用 AddressValidator 类中所包含的逻辑,而不采用复制和粘贴的方式,你可以在 CustomerValidator 中依赖注入或实例化一个 AddressValidator,如下面的例子所示:
public class CustomerValidator implements Validator {
private final Validator addressValidator;
public CustomerValidator(Validator addressValidator) {
if (addressValidator == null) {
throw new IllegalArgumentException("The supplied [Validator] is " +
"required and must not be null.");
}
// 如果传入的验证器不支持 Address 地址
if (!addressValidator.supports(Address.class)) {
throw new IllegalArgumentException("The supplied [Validator] must " +
"support the validation of [Address] instances.");
}
this.addressValidator = addressValidator;
}
/**
* 这个验证器可以验证客户实例,以及客户的任何子类。
*/
public boolean supports(Class clazz) {
return Customer.class.isAssignableFrom(clazz);
}
public void validate(Object target, Errors errors) {
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "firstName", "field.required");
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "surname", "field.required");
Customer customer = (Customer) target;
try {
// 验证下一个嵌套对象
errors.pushNestedPath("address");
// 这里使用传入的验证器进行验证
ValidationUtils.invokeValidator(this.addressValidator, customer.getAddress(), errors);
} finally {
errors.popNestedPath();
}
}
}
验证错误被报告给传递给验证器的 Errors 对象。在 Spring Web MVC 中,你可以使用 <spring:bind/>
标签来检查错误信息,但你也可以自己检查 Errors 对象。关于它提供的方法的更多信息可以在 javadoc 中找到。