本篇文章主要讲解Spring 提供的对象合法性验证接口org.springframework.validation.Validator
,顺带提及一下javax.validation.Validator
,注意本篇文章未加特别说明的Validator
一律指org.springframework.validation.Validator
接口定义
org.springframework.validation.Validator
Validator
是Spring 为我们提供了一套验证对象的接口,接口定义如下:
public interface Validator {
boolean supports(Class> clazz);
void validate(Object target, Errors errors);
}
从定义上我们可以猜到Validator
主要就是验证对象是否合法,并将验证结果添加到Errors
对象中。多的不解释,先上几行代码!
// (1) javax.validation.Validator
private final Validator nativeValidator = Validation.buildDefaultValidatorFactory().getValidator();
// (2) SpringValidatorAdapter 对象实现了javax.validation.Validator 和 org.springframework.validation.Validator两接口
private final SpringValidatorAdapter validatorAdapter = new SpringValidatorAdapter(nativeValidator);
//messageSource
private final StaticMessageSource messageSource = new StaticMessageSource();
@Test // SPR-13406
public void testNoStringArgumentValue() throws Exception {
// (3) 需要被验证的对象
TestBean testBean = new TestBean();
testBean.setPassword("pass");
testBean.setConfirmPassword("pass");
// (4) Error对象,用于存储验证错误信息
BeanPropertyBindingResult errors = new BeanPropertyBindingResult(testBean, "testBean");
//实际验证方法
validatorAdapter.validate(testBean, errors);
//获取验证结果
assertThat(errors.getFieldErrorCount("password"), is(1));
assertThat(errors.getFieldValue("password"), is("pass"));
FieldError error = errors.getFieldError("password");
assertNotNull(error);
// (5) 利用messageSource获取错误描述信息
assertThat(messageSource.getMessage(error, Locale.ENGLISH), is("Size of Password must be between 8 and 128"));
//检查错误原因
assertTrue(error.contains(ConstraintViolation.class));
//获取具体错误原因对象
assertThat(error.unwrap(ConstraintViolation.class).getPropertyPath().toString(), is("password"));
assertThat(SerializationTestUtils.serializeAndDeserialize(error.toString()), is(error.toString()));
}
以上是org.springframework.validation.beanvalidation2.SpringValidatorAdapterTests
单元测试片段代码,我们主要从上面标注的([1,2,3,4,5])
来入手了解我们的Spring Validator的功能与原理
(1) javax.validation.Validator
这是validation-api
中的比较重量级的接口,其功能也是验证对象的合法性,Constrain once, validate everywhere
这是官网标语。我们在来看一张图
这张图明确表明了,
validation-api
版本与JSR对应关系,如果不知道JSR是什么,老铁这里不用纠结,你只需要知道,这里的JSR就是验证对象合法性的一些规范(而实现了这套规范的有hibernate-validator
)如下图是一些注解规范:
这些规范语义很清楚,就不过多解释。那么
javax.validation.Validator
为什么会出现在这里了?很简单,一句话不要重复造轮子
在这里体现,也就是说Spring 的Validator
是包含了我们JSR303
,JSR349
等规范了的,并且还是使用的hibernate-validator
实现。
(2) SpringValidatorAdapter
SpringValidatorAdapter是Validator
的一个实现类,我们在看看其类图,可以知道SpringValidatorAdapter
是实现了Validator
和javax.validation.Validator
两个接口的。
(3) 需要被验证的对象
我们看看TestBean
的定义
@Same(field = "password", comparingField = "confirmPassword")
@Same(field = "email", comparingField = "confirmEmail")
static class TestBean {
@Size(min = 8, max = 128)
private String password;
private String confirmPassword;
@Pattern(regexp = "[\\w.'-]{1,}@[\\w.'-]{1,}")
private String email;
@Pattern(regexp = "[\\p{L} -]*", message = "Email required")
private String confirmEmail;
}
可以看到这个类已经被很多注解修饰,这里的注解也就是我们需要验证的规则。@Size
和@Pattern
是validator-api
中定义的hibernate-validator
已经完全实现了验证功能,而@Same
是Spring Test自定义的
注解,如下:
@Documented
@Constraint(validatedBy = {SameValidator.class})
@Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(SameGroup.class)
@interface Same {
。。。。
}
这里我们重点看@Constraint(validatedBy = {SameValidator.class})
这就是@Same
验证逻辑的实现类,当然这个类必须实现ConstraintValidator
接口,以下是接口定义
public interface ConstraintValidator {
//注解的元信息
default void initialize(A constraintAnnotation) {
}
//验证方法
boolean isValid(T value, ConstraintValidatorContext context);
}
(4) Errors对象,用于存储验证错误信息
通过下图左边可以看到Errors接口提供了添加错误信息
和获取错误信息
的接口,这也是Spring为我们提供的统一处理和获取Error信息的接口。而右边就是我们BeanPropertyBindingResult
的一个关系类图,也就Errors
接口的具体实现类。
(5) 利用messageSource获取错误描述信息
这里很和谐的和Spring的MessageResource
搭配使用。以下是DefaultMessageCodesResolver.resolveMessageCodes
方法为生成code的规则。
总结
现在我们在对Spring Validator
的功能做次总结
- 支持 hibernate-validate 实现的 validator-api
- 对错误信息提供了友好的封装
- 错误信息能够无缝结合MessageSource,以便提供国际化支持
使用
Service 中使用
@Service
@Validated
//Validated 注意 一定要在类上 因为切面是用的
//Pointcut pointcut = new AnnotationMatchingPointcut(this.validatedAnnotationType, true)
// 代码源自MethodValidationPostProcessor
public class HelloService {
//参数使用 Valid
public void say(@Valid Hello hello) {
System.out.println("xxx");
}
}
Controller 中使用
@RequestMapping("say")
//此处使用Validated 或者 Valid 都可以
//ModelAttributeMethodProcessor.determineValidationHints 判断 Validated 或者 Valid
//使用Errors 可以接收错误信息,不用Errors会直接抛异常到前端
public String say(@Validated Hello hehe, Errors errors)
感谢
感谢各位老铁花时间观看!
欢迎留言指正!
内容持续更新!