前言
上篇文章 已经介绍了Bean Validation它的概念、JSR标准,也已经感受了一把使用它来对JavaBean进行校验。本文将继续讲解它的余下执行过程~
在这里先说一句,因为Bean Validation涉及到的API关键类实在是太多了(感叹:hibernate validation实现这一套复杂度非常之高),为此我专门写了一个关键类打点篇,若不熟悉关键组件的,本人强烈建议先花几分钟去浏览一下:深入了解数据校验(Bean Validation):基础类打点(ValidationProvider、ConstraintDescriptor、ConstraintValidator)【享学Java】
上文讲到已经通过配置拿到了ValidatorFactory,本文接着此处继续。
说明:下面的讲解基于此案例:
@Getter @Setter @ToString public class Person { // 错误消息message是可以自定义的 @NotNull(message = "{message} -> 名字不能为null") public String name; @Max(10) @Positive public Integer age; @NotNull @NotEmpty private List<@Email String> emails; @Future private Date start; } public static void main(String[] args) { Person person = new Person(); //person.setName("fsx"); person.setAge(18); // email校验:虽然是List都可以校验哦 person.setEmails(Arrays.asList("fsx@gmail.com", "baidu@baidu.com", "aaa.com")); //person.setStart(new Date()); //start 需要是一个将来的时间: Sun Jul 21 10:45:03 CST 2019 //person.setStart(new Date(System.currentTimeMillis() + 10000)); //校验通过 HibernateValidatorConfiguration configure = Validation.byProvider(HibernateValidator.class).configure(); ValidatorFactory validatorFactory = configure.failFast(false).buildValidatorFactory(); // 根据validatorFactory拿到一个Validator Validator validator = validatorFactory.getValidator(); // 使用validator对结果进行校验 Set<ConstraintViolation<Person>> result = validator.validate(person); // 对结果进行遍历输出 result.stream().map(v -> v.getPropertyPath() + " " + v.getMessage() + ": " + v.getInvalidValue()) .forEach(System.out::println); }
运行打印:
name {message} -> 名字不能为null -> 名字不能为null: null age 最大不能超过10: 18 emails[2].<list element> 不是一个合法的电子邮件地址: aaa.com
ValidatorFactory:验证器工厂
在准备好了一个javax.validation.Configuration后,接下来最重要的事就是根据配置Configuration拿到一个ValidatorFactory,进而拿到javax.validation.Validator完成校验~
此类从命名上看非常简单:Validator工厂
public interface ValidatorFactory extends AutoCloseable { // 显然,这个接口是最为重要的 Validator getValidator(); // 定义一个新的ValidatorContext验证器上下文,并且和Validator关联上 ValidatorContext usingContext(); // 下面这些获取 不用过多解释了~ MessageInterpolator getMessageInterpolator(); TraversableResolver getTraversableResolver(); ConstraintValidatorFactory getConstraintValidatorFactory(); ParameterNameProvider getParameterNameProvider(); ClockProvider getClockProvider(); public <T> T unwrap(Class<T> type); // 复写AutoCloseable的方法 @Override public void close(); }
看看它的继承树:
它的实现主要有Spring的实现和Hibernate Validation的实现。因为Spring后续还有专题非常详细的描述,因此本文就只关注Hibernate的实现了~
HibernateValidatorFactory
它是Hibernate Validation提供的,继承自标准接口ValidatorFactory,在原基础上进行了扩展:对脚本进行支持,以及支持Duration事件误差~
public interface HibernateValidatorFactory extends ValidatorFactory { // 它用于支持脚本。支持使用注解@ScriptAssert等(使用太少了) // 关于Java运行脚本,可参阅javax.script.ScriptEngineManager @Incubating ScriptEvaluatorFactory getScriptEvaluatorFactory(); // 这主要是处理date/time的误差问题~~~ @Incubating Duration getTemporalValidationTolerance(); // 复写父接口的方法,使用更精确的HibernateValidatorContext验证器上下文 @Override HibernateValidatorContext usingContext(); }
关于唯一实现类:ValidatorFactoryImpl
public class ValidatorFactoryImpl implements HibernateValidatorFactory { ... // 省略非常多的成员变量 // 唯一的构造函数,做了非常非常多初始化的事~~~ public ValidatorFactoryImpl(ConfigurationState configurationState) { ... } // 这个或许是最重要的一个方法 @Override public Validator getValidator() { return createValidator( constraintValidatorManager.getDefaultConstraintValidatorFactory(), valueExtractorManager, validatorFactoryScopedContext, methodValidationConfiguration ); } Validator createValidator(ConstraintValidatorFactory constraintValidatorFactory, ValueExtractorManager valueExtractorManager, ValidatorFactoryScopedContext validatorFactoryScopedContext, MethodValidationConfiguration methodValidationConfiguration) { BeanMetaDataManager beanMetaDataManager = beanMetaDataManagers.computeIfAbsent( new BeanMetaDataManagerKey( validatorFactoryScopedContext.getParameterNameProvider(), valueExtractorManager, methodValidationConfiguration ), key -> new BeanMetaDataManager( constraintHelper, executableHelper, typeResolutionHelper, validatorFactoryScopedContext.getParameterNameProvider(), valueExtractorManager, validationOrderGenerator, buildDataProviders(), methodValidationConfiguration ) ); return new ValidatorImpl( constraintValidatorFactory, beanMetaDataManager, valueExtractorManager, constraintValidatorManager, validationOrderGenerator, validatorFactoryScopedContext ); } @Override public MessageInterpolator getMessageInterpolator() { return validatorFactoryScopedContext.getMessageInterpolator(); } @Override public TraversableResolver getTraversableResolver() { return validatorFactoryScopedContext.getTraversableResolver(); } ... @Override public <T> T unwrap(Class<T> type) { if ( type.isAssignableFrom( HibernateValidatorFactory.class ) ) { return type.cast( this ); } throw LOG.getTypeNotSupportedForUnwrappingException( type ); } @Override public HibernateValidatorContext usingContext() { return new ValidatorContextImpl( this ); } ... }
此验证器工厂的实现类源码,不可为不复杂(复杂代码本文并没有贴出),我反复看了几遍仍还处于一个准懵逼的状态。
为此我觉得应该关注点,基于打点文章里已经介绍了关键的各个组件,so我不在纠结于实现(创建)细节,只需要着重关注Validator验证器本身了。
说明:Spring对ValidatorFactory的实现稍微简单点,但也不会太容易。因为绝大多数我们在使用Spring,因此在Spring章节此处不会放过~
Validator:验证器
官方的解释简单明了:校验Bean实例~ ,介绍得非常简单但却又是这么回事有木有
到此处,就正式和Bean的校验开始打交道了,也是我们最直接能出效果的一个API,所以说它是最重要的其实也不过为
public interface Validator { // 校验作用在此Bean上面的所有约束(所有属性、方法、构造器的所有约束) // groups可以指定只使用某个group,默认是Defualt的group嘛~ <T> Set<ConstraintViolation<T>> validate(T object, Class<?>... groups); // 上面太过于粗暴。这里是校验这个Bean上 某个具体的属性~ <T> Set<ConstraintViolation<T>> validateProperty(T object, String propertyName, Class<?>... groups); // 这个就更加精确了,具体的属性的具体value值都要校验 <T> Set<ConstraintViolation<T>> validateValue(Class<T> beanType, String propertyName, Object value, Class<?>... groups); // 返回描述Bean约束的描述符对象,此对象和ConstraintDescriptor关联 // 并且还持有PropertyDescriptor和ConstructorDescriptor等等~ BeanDescriptor getConstraintsForClass(Class<?> clazz); <T> T unwrap(Class<T> type); // 返回用于验证方法和构造函数的参数和返回值的协定。 // 不巧的是:ValidatorImpl实现了Validator, ExecutableValidator这两个接口 ExecutableValidator forExecutables(); } // ================关于ExecutableValidator接口================ // 它用于验证 方法 和 构造函数 的 **参数和返回值**。 public interface ExecutableValidator { // 验证方法的入参 <T> Set<ConstraintViolation<T>> validateParameters(T object, Method method, Object[] parameterValues, Class<?>... groups); // 验证方法的返回值 <T> Set<ConstraintViolation<T>> validateReturnValue(T object, Method method, Object returnValue, Class<?>... groups); // 不解释~~~~~~~~~~~~ <T> Set<ConstraintViolation<T>> validateConstructorParameters(Constructor<? extends T> constructor, Object[] parameterValues, Class<?>... groups); <T> Set<ConstraintViolation<T>> validateConstructorReturnValue(Constructor<? extends T> constructor, T createdObject, Class<?>... groups); }
Spring对它的实现非常丰富,但本文还是只看ValidatorImpl。