深入了解数据校验(Bean Validation):ValidatorFactory和Validator等核心API【享学Java】(上)

简介: 深入了解数据校验(Bean Validation):ValidatorFactory和Validator等核心API【享学Java】(上)

前言


上篇文章 已经介绍了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();
}


看看它的继承树:image.png


它的实现主要有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);
}

image.png


Spring对它的实现非常丰富,但本文还是只看ValidatorImpl。

目录
打赏
0
0
0
0
37
分享
相关文章
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
141 2
Java爬虫获取微店店铺所有商品API接口设计与实现
本文介绍如何使用Java设计并实现一个爬虫程序,以获取微店店铺的所有商品信息。通过HttpClient发送HTTP请求,Jsoup解析HTML页面,提取商品名称、价格、图片链接等数据,并将其存储到本地文件或数据库中。文中详细描述了爬虫的设计思路、代码实现及注意事项,包括反爬虫机制、数据合法性和性能优化。此方法可帮助商家了解竞争对手,为消费者提供更全面的商品比较。
java语言后台管理若依框架-登录提示404-接口异常-系统接口404异常如何处理-登录验证码不显示prod-api/captchaImage 404 (Not Found) 如何处理-解决方案优雅草卓伊凡
java语言后台管理若依框架-登录提示404-接口异常-系统接口404异常如何处理-登录验证码不显示prod-api/captchaImage 404 (Not Found) 如何处理-解决方案优雅草卓伊凡
86 5
Java基础-常用API-Object类
继承是面向对象编程的重要特性,允许从已有类派生新类。Java采用单继承机制,默认所有类继承自Object类。Object类提供了多个常用方法,如`clone()`用于复制对象,`equals()`判断对象是否相等,`hashCode()`计算哈希码,`toString()`返回对象的字符串表示,`wait()`、`notify()`和`notifyAll()`用于线程同步,`finalize()`在对象被垃圾回收时调用。掌握这些方法有助于更好地理解和使用Java中的对象行为。
|
7月前
|
告别繁琐编码,拥抱Java 8新特性:Stream API与Optional类助你高效编程,成就卓越开发者!
【8月更文挑战第29天】Java 8为开发者引入了多项新特性,其中Stream API和Optional类尤其值得关注。Stream API对集合操作进行了高级抽象,支持声明式的数据处理,避免了显式循环代码的编写;而Optional类则作为非空值的容器,有效减少了空指针异常的风险。通过几个实战示例,我们展示了如何利用Stream API进行过滤与转换操作,以及如何借助Optional类安全地处理可能为null的数据,从而使代码更加简洁和健壮。
183 0
|
3月前
|
如何使用Java开发获得淘宝商品描述API接口?
本文详细介绍如何使用Java开发调用淘宝商品描述API接口,涵盖从注册淘宝开放平台账号、阅读平台规则、创建应用并申请接口权限,到安装开发工具、配置开发环境、获取访问令牌,以及具体的Java代码实现和注意事项。通过遵循这些步骤,开发者可以高效地获取商品详情、描述及图片等信息,为项目和业务增添价值。
130 10
Java中的Lambda表达式与Stream API的协同作用
在本文中,我们将探讨Java 8引入的Lambda表达式和Stream API如何改变我们处理集合和数组的方式。Lambda表达式提供了一种简洁的方法来表达代码块,而Stream API则允许我们对数据流进行高级操作,如过滤、映射和归约。通过结合使用这两种技术,我们可以以声明式的方式编写更简洁、更易于理解和维护的代码。本文将介绍Lambda表达式和Stream API的基本概念,并通过示例展示它们在实际项目中的应用。
探索Java中的Lambda表达式与Stream API
【10月更文挑战第22天】 在Java编程中,Lambda表达式和Stream API是两个强大的功能,它们极大地简化了代码的编写和提高了开发效率。本文将深入探讨这两个概念的基本用法、优势以及在实际项目中的应用案例,帮助读者更好地理解和运用这些现代Java特性。
|
4月前
|
Java中的Lambda表达式与Stream API的高效结合####
探索Java编程中Lambda表达式与Stream API如何携手并进,提升数据处理效率,实现代码简洁性与功能性的双重飞跃。 ####
48 0
大数据-147 Apache Kudu 常用 Java API 增删改查
大数据-147 Apache Kudu 常用 Java API 增删改查
66 1

热门文章

最新文章

AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等