4. Validator 校验器的五大核心组件,一个都不能少
困难是弹簧,你弱它就强。本文已被 https://www.yourbatman.cn 收录,里面一并有Spring技术栈、MyBatis、JVM、中间件等小而美的专栏供以免费学习。关注公众号【BAT的乌托邦】逐个击破,深入掌握,
✍前言
你好,我是YourBatman。
[上篇文章]()介绍了校验器上下文ValidatorContext,知道它可以对校验器Validator的核心五大组件分别进行定制化设置,那么这些核心组件在校验过程中到底扮演着什么样的角色呢,本文一探究竟。
作为核心组件,是有必要多探究一分的。以此为基,再扩散开了解和使用其它功能模块便将如鱼得水。但是过程枯燥是真的,所以需要坚持呀。
版本约定
Bean Validation版本:
2.0.2
Hibernate Validator版本:
6.1.5.Final
✍正文
Bean Validation校验器的这五大核心组件通过ValidatorContext可以分别设置:若没设置(或为null),那就回退到使用ValidatorFactory默认的组件。
准备好的组件,统一通过ValidatorFactory暴露出来予以访问:
MessageInterpolator
直译为:消息插值器。按字面不太好理解:简单的说就是对message内容进行格式化,若有占位符{}
或者el表达式${}
就执行替换和计算。对于语法错误应该尽量的宽容。
校验失败的消息模版交给它处理就成为了人能看得懂的消息格式,因此它能够处理消息的国际化:消息的key是同一个,但根据不同的Locale展示不同的消息模版。最后在替换/技术模版里面的占位符即可~
这是Bean Validation的标准接口,Hibernate Validator提供了实现:
Hibernate Validation它使用的是ResourceBundleMessageInterpolator来既支持参数,也支持EL表达式。内部使用了javax.el.ExpressionFactory这个API来支持EL表达式${}
的,形如这样:must be greater than ${inclusive == true ? 'or equal to ' : ''}{value}
它是能够动态计算出${inclusive == true ? 'or equal to ' : ''}
这部分的值的。
接口方法直接了当:根据上下文Context填充消息模版messageTemplate。它的具体工作流程我用图示如下:
context
上下文里一般是拥有需要被替换的key的键值对的,如下图所示:
Hibernate对Context的实现中扩展出了如图的两个Map(非JSR标准),可以让你优先于 constraintDescriptor取值,取不到再fallback到标准模式的ConstraintDescriptor
里取值,也就是注解的属性值。具体取值代码如下:
大部分情况下我们只用得到注解属性里面的值,也就是错误消息里可以使用{注解属性名}
这种方式动态获取到注解属性值,给与友好错误提示。
上下文里的Message参数和Expression参数如何放进去的?在后续高级使用部分,会自定义k-v替换参数,也就会使用到本部分的高级应用知识,后文见。
TraversableResolver
能跨越的处理器。从字面是非常不好理解,用粗暴的语言解释为:确定某个属性是否能被ValidationProvider访问,当妹访问一个属性时都会通过它来判断一下子,提供两个判断方法:
该接口主要根据配置项来进行判断,并不负责。内部使用,调用者基本无需关心,也不见更改其默认机制,暂且略过。
ConstraintValidatorFactory
约束校验器工厂。ConstraintValidator约束校验器我们应该不陌生:每个约束注解都得指定一个/多个约束校验器,形如这样:@Constraint(validatedBy = { xxx.class })
。
ConstraintValidatorFactory就是工厂:可以根据Class生成对象实例。
Hibernate提供了唯一实现ConstraintValidatorFactoryImpl:使用空构造器生成实例 clazz.getConstructor().newInstance();
。
小贴士:接口并没规定你如何生成实例,Hibernate Validator是使用空构造这么实现的而已~
ParameterNameProvider
参数名提供器。这个组件和Spring的ParameterNameDiscoverer
作用是一毛一样的:获取方法/构造器的参数名。
提供的实现:
DefaultParameterNameProvider
:基于Java反射APIExecutable#getParameters()
实现
运行程序,输出:
一样的,若你想要打印出明确的参数名,请在编译参数上加上-parameters
参数。
ReflectionParameterNameProvider
:已过期。请使用上面的default代替ParanamerParameterNameProvider
:基于com.thoughtworks.paranamer.Paranamer
实现参数名的获取,需要额外导入相应的包才行。嗯,这里我就不试了哈~
ClockProvider
时钟提供器。这个接口很简单,就是提供一个Clock,给@Past、@Future
等阅读判断提供参考。唯一实现为DefaultClockProvider:
默认使用当前系统时钟作为参考。若你的系统有全局统一的参考标准,比如统一时钟,那就可以通过此接口实现自己的Clock时钟,毕竟每台服务器的时间并不能保证是完全一样的不是,这对于时间敏感的应用场景(如竞标)需要这么做。
以上就是对Validator校验器的五个核心组件的一个描述,总体上还是比较简单。其中第一个组件:MessageInterpolator插值器我认为是最为重要的,需要理解好了。对后面做自定义消息模版、国际化消息都有用。
加餐:ValueExtractor
值提取器。2.0版本新增一个比较重要的组件API,作用:把值从容器内提取出来。这里的容器包括:数组、集合、Map、Optional等等。
容易想到,ValueExtractor的实现类就非常之多(所有的实现类都是内建的,非public的,这就是默认情况下支持的容器类型):
举例两个典型实现:
校验器Validator通过它把值从容器内提取出来参与校验,从这你应该就能理解为毛从Bean Validation2.0开始就支持验证容器内的元素了吧,形如这样:List<@NotNull @Valid Person>、Optional<@NotNull @Valid Person>
,可谓大大的方便了使用。
若你有自定义容器,需要提取的需求,那么你可以自定义一个
ValueExtractor
实现,然后通过ValidatorContext#addValueExtractor()
添加进去即可
✍总结
本文主要介绍了Validator校验器的五大核心组件的作用,Bean Validation2.0提供了ValueExtractor组件来实现容器内元素的校验,大大简化了对容器元素的校验复杂性,值得点赞。
✔推荐阅读:
---------
♥关注A哥♥
Author | A哥(YourBatman)
-------- | -----
个人站点 | www.yourbatman.cn
E-mail | yourbatman@qq.com
微 信 | fsx641385712
公众号 | BAT的乌托邦(ID:BAT-utopia)
知识星球 | BAT的乌托邦
每日文章推荐 | 每日文章推荐
版权声明: 本文为 InfoQ 作者【YourBatman】的原创文章。
原文链接:【http://xie.infoq.cn/article/3cfb3e033a887645923e82791】。文章转载请联系作者。
评论