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*/@Overridepublic boolean supports(Class<?> clazz) {return Person.class.equals(clazz);}@Overridepublic 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,还有其他的,这个下一个章节会讲为什么会多出来两个 codeField 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 中找到。
