java开发工具(7)Bean validation校验源码解析

因为上一篇博文讲到了Bean Validation在SpringBoot中的使用,为了让你知其然而知其所以然,我决定写一篇相关的源码解读,在这里让你完全理解Bean Validation的使用原理。

在了解这篇文章前,你需要先知道,Spring是如何将get请求中url里面的入参映射到类中的,是通过HandlerMethodArgumentResolver类的resolveArgument方法。在Spring中默认执行接口HandlerMethodArgumentResolver类的resolveArgument方法的实现类是ModelAttributeMethodProcessor类。

一、渲染入参,进行校验

下面的红框中就是在代码渲染完成后进行了校验,这个方法是在resolveArgument方法中
入参校验
最后通过一系列的方法调用,走到了hibernate-validator包中的ValidatorImpl类的validate方法中,到这一步,方法调用的时序图如下:

ModelAttribute DateBinder ValidatorAdapter SpringValid ValidatorImpl validateIfApplicable(binder, parameter) binder.validate(validationHints) validator.validate(target, bindingResult) this.target.validate(target, errors) this.targetValidator.validate(target) ModelAttribute DateBinder ValidatorAdapter SpringValid ValidatorImpl

此时,你肯定有疑问了,怎么从Spring的Validator最后调用到了hibernate-validator包中的Validator中的呢?

二、从Spring到hibernate-validator

1、入口

这一切都要从spring-context中的一个类说起,即LocalValidatorFactoryBean,这个类就是负责加载外部实现的验证类到Spring中,它有一个方法afterPropertiesSet。
afterPropertiesSet
这个方法属性Spring的都不会陌生,这个方法是Spring的主流程执行完会执行的方法。这个流程会走到2处,即:

configuration = bootstrap.configure();

2、加载外部实现类

最后会进入Validation类的run方法中:
Validation
上图中,真正去加载验证类的,是这行代码:

List<ValidationProvider<?>> cachedContextClassLoaderProviderList = getCachedValidationProviders( classloader );

这行代码中,使用了一个参数classLoader,是通过

ClassLoader classloader = Thread.currentThread().getContextClassLoader();

关于ContextClassLoader

获取的,可以看到这是一个ContextClassLoader,这里关于类加载器涉及到双亲委派模型的只能从下级ClassLoader去向上级ClassLoader的问题,ContextClassLoader就解决了,怎么让上层的ClassLoader加载的类,使用下级的类加载的ClassLoader,于是便将ContextClassLoader放到了当前线程中,在这里你只需要知道ContextClassLoader是为了能让Spring加载下级的jar包中类即可。

加载Hibernate中的类

真正加载Hebernate-validator包的代码调用是这一段方法:

private synchronized List<ValidationProvider<?>> getCachedValidationProviders(ClassLoader classLoader) {
	SoftReference<List<ValidationProvider<?>>> ref = providersPerClassloader.get( classLoader );
	return ref != null ? ref.get() : null;
}

可以看到这样代码会加载jar包中,ValidationProvider实现类,而hibernate-validator包中的HibernateValidator就实现了ValidationProvider接口,这样类就被加载进来了。

后面的流程就是根据HibernateValidator去实现相应的方法了。关于Bean Validation的各种工具流程,这里有一篇比较全面的文章.,结合我的源码讲解去看,你会了解的更加全面。

三、hibernate-validator中的流程

从Spring到hibernate-validator的流程就说完了,后面的执行就在hibernate-validator包中了,此后的逻辑我大致概括下:

  1. 根据group分组去验证,如果没有group分组就默认验证,
  2. 到指定的Validator验证类中去验证
  3. 如果验证失败返回错误信息
    我们看下源码:

验证当前分组:
分组
最后走到了NotNullValidator中的isValid方法,
NotNullValidator
这其中的时序图如下:

ValidatorImpl MetaConstraint ConstraintTree SimpleCons NotNullValidator validateInContext() metaConstraint.validateConstraint() constraintTree.validateConstraints() validateConstraints() validateSingleConstraint() validator.isValid() 因为使用的是@NotNu ll注解所以到了NotN ullValidator ValidatorImpl MetaConstraint ConstraintTree SimpleCons NotNullValidator

四、各种设计原则

到这里流程算是告一段落,但是我们的分析还没完,在整个流程中涉及到了多种设计原则,搞懂流程不算源码,搞懂其中牵涉到的各种设计原则和模式才算让这段源码了然于胸。

  • 单一职责原则:在这些Validator中,一个NotNull就是一个Validator,各种验证互相独立,层次分明
  • 接口隔离原则:所有Validator都是实现ConstraintValidator接口,易于扩展
  • 开闭原则:Spring通过ContextClassLoader去加载验证的模块,当新增验证模块的时候不需要修改Spring代码,只需要增加验证模块即可
  • 迪米特法则:ModelAttributeMethodProcessor类实现了HandlerMethodArgumentResolver,HandlerMethodReturnValueHandler,分别处理数据的渲染和参数的返回。
    设计模式:
  • 策略模式:通过条件筛选对应的Validator,当新增验证方式的时候,只需要新增Validator即可。
  • 单例模式:每个Validator只创建一个,缓存在内存的Map中,需要使用的时候直接获取。
发布了188 篇原创文章 · 获赞 117 · 访问量 38万+

猜你喜欢

转载自blog.csdn.net/lz710117239/article/details/104585752