深入了解数据校验(Bean Validation):基础类打点(ValidationProvider、ConstraintDescriptor、ConstraintValidator)【享学Java】(下)

简介: 深入了解数据校验(Bean Validation):基础类打点(ValidationProvider、ConstraintDescriptor、ConstraintValidator)【享学Java】(下)

ValidatorContext:验证器上下文


创建Validator的上下文,例如,建立不同的消息插值器或可遍历分解器。

public interface ValidatorContext {
  ValidatorContext messageInterpolator(MessageInterpolator messageInterpolator);
  ValidatorContext traversableResolver(TraversableResolver traversableResolver);
  ValidatorContext constraintValidatorFactory(ConstraintValidatorFactory factory);
  ValidatorContext parameterNameProvider(ParameterNameProvider parameterNameProvider);
  // @since 2.0
  ValidatorContext clockProvider(ClockProvider clockProvider);
  ValidatorContext addValueExtractor(ValueExtractor<?> extractor);
  // 最终的方法
  Validator getValidator();
}


可以看到这个的API和javax.validation.Configuration是何其相似。它的继承数如下:


image.png


HibernateValidatorContext子接口对此进行了增强,具体增强了哪些方法,请参考HibernateValidatorConfiguration,一毛一样的~

关于它的实现类ValidatorContextImpl,此处只介绍一个方法:


public class ValidatorContextImpl implements HibernateValidatorContext {
  // 创建一个Validator
  private final ValidatorFactoryImpl validatorFactory;
  // 它加入了上面所有的内置的值提取器
  private final ValueExtractorManager valueExtractorManager;
  // 拿到一个校验器  使用ValidatorFactory  Validator就是最终对Bean进行校验的东西  
  // 它持有各种上下文,各种插值器、提取器等等
  @Override
  public Validator getValidator() {
    return validatorFactory.createValidator(
        constraintValidatorFactory,
        valueExtractorDescriptors.isEmpty() ? valueExtractorManager : new ValueExtractorManager( valueExtractorManager, valueExtractorDescriptors ),
        validatorFactoryScopedContextBuilder.build(),
        methodValidationConfigurationBuilder.build()
    );
  }
}



从源码中可以看出来,hibernate validation它也是支持JavaFX的校验的~


ConstraintHelper:约束帮助类


关于内置的这些校验注解,我们可以看到注解上面并没有指定:@Constraint(validatedBy = { }),那你就参考此类吧,它把内置的ConstraintValidator都早早就注册上去了~~~


public class ConstraintHelper {
  public ConstraintHelper() {
    // 每个注解对应有多个校验器。(因为每个注解都可能标注在多种类型上嘛~)
    Map<Class<? extends Annotation>, List<ConstraintValidatorDescriptor<?>>> tmpConstraints = new HashMap<>();
    putConstraint( tmpConstraints, AssertFalse.class, AssertFalseValidator.class );
    putConstraint( tmpConstraints, AssertTrue.class, AssertTrueValidator.class );
    ... // 非常非常多的putConstraint() 方法~~~~
  }
}

归纳总结一下,每个校验注解都必备的三个属性如下:


@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
@Repeatable(List.class)
@Documented
@Constraint(validatedBy = { })
public @interface AssertFalse {
  // 这三个属性是所有校验注解必须的,其余的注解根据具体情况自己自定义~~~~~
  String message() default "{javax.validation.constraints.AssertFalse.message}";
  Class<?>[] groups() default { };
  Class<? extends Payload>[] payload() default { };
  // Defines several {@link AssertFalse} annotations on the same 
  @Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
  @Retention(RUNTIME)
  @Documented
  @interface List {
    AssertFalse[] value();
  }
}


若自定义注解的话,自己可以随便定义属性,并且实现ConstraintValidator接口实现个校验器书写校验逻辑,写在注解上@Constraint(validatedBy = { })即可生效~~~


ConstraintViolation:约束冲突


描述约束冲突。此对象公开约束冲突上下文以及描述冲突的消息。

public interface ConstraintViolation<T> {
  // 返回已经插值过的错误消息
  String getMessage();
  // 未插值过的原始消息模版
  String getMessageTemplate();
  // 校验的Root Bean(若是校验方法那就是方法所在的Bean)
  T getRootBean();
  Class<T> getRootBeanClass();
  Object getLeafBean();
  Object[] getExecutableParameters();
  Object getExecutableReturnValue();
  // the property path to the value from {@code rootBean}
  Path getPropertyPath();
  // 验证木有通过的值
  Object getInvalidValue();
  // 这个很重要~~~信息量很大
  ConstraintDescriptor<?> getConstraintDescriptor();
  <U> U unwrap(Class<U> type);
}


它的继承树如下


image.png


子接口HibernateConstraintViolation增加了一个方法:


public interface HibernateConstraintViolation<T> extends ConstraintViolation<T> {
  <C> C getDynamicPayload(Class<C> type);
}


关于唯一实现类ConstraintViolationImpl呢,没啥好说的,逻辑都不在它着,在调用它的地方~ 根据ValidationOperation选择~


ConstraintValidatorContext:约束验证上下文


在应用给定的约束验证器(ConstraintValidator)时,提供上下文数据和操作。正所谓每一个约束(注解)都至少对应一个ConstraintValidator嘛~


我敢说,哪怕你是自己在自定义约束验证器,但是你都很少使用这个上下文。但如果使用好了,效果是非常客观的~


public interface ConstraintValidatorContext {
  // 禁用默认的错误时生成`ConstraintViolation`的方式(默认是使用message嘛)
  // 它的作用是:自己根据不同的message、或者不同的属性来生成不同的ConstraintViolation
  void disableDefaultConstraintViolation();
  // 未插值的message:constraintDescriptor.getMessageTemplate()
  String getDefaultConstraintMessageTemplate();
  ClockProvider getClockProvider();
  // 关于ConstraintViolationBuilder此处就不能在展开了,功能大强大  使用起来也太复杂了
  // 它的作用就是根据message模版,来添加和生成一个ConstraintViolation
  // ConstraintViolationBuilder 可以设置各种参数~~~
  ConstraintViolationBuilder buildConstraintViolationWithTemplate(String messageTemplate);
  <T> T unwrap(Class<T> type);
}


它的继承树如下:image.png


子接口:HibernateConstraintValidatorContext


// 它增强的作用就是能给插值的时候提供更多的参数
public interface HibernateConstraintValidatorContext extends ConstraintValidatorContext {
  // 为约束冲突的message插值提供额外的参数。它能作用于为该约束生成的**所有约束冲突**
  // 它包括默认值以及通过ConstraintViolationBuilder创建出来的所有冲突
  // 要使用不同的变量值**创建多个约束冲突**,可以在对ConstraintViolationBuilder#addConstraintViolation()连续调用之间调用此方法~~~
  HibernateConstraintValidatorContext addMessageParameter(String name, Object value);
  // 基本同上,只是上面处理的是{},这里处理的是${}  el表达式
  HibernateConstraintValidatorContext addExpressionVariable(String name, Object value);
  // 允许设置可以进一步描述冲突的对象。
  HibernateConstraintValidatorContext withDynamicPayload(Object payload);
  // 返回指定ConstraintValidator约束校验器的负载
  // @since 6.0.9
  @Incubating
  <C> C getConstraintValidatorPayload(Class<C> type);
}


关于实现类:ConstraintValidatorContextImpl


public class ConstraintValidatorContextImpl implements HibernateConstraintValidatorContext {
  private final ConstraintDescriptor<?> constraintDescriptor;
  // 显然  默认值是false
  @Override
  public final void disableDefaultConstraintViolation() {
    defaultDisabled = true;
  }
  @Override
  public final String getDefaultConstraintMessageTemplate() {
    return constraintDescriptor.getMessageTemplate();
  }
  @Override
  public <T> T unwrap(Class<T> type) {
    if ( type.isAssignableFrom( HibernateConstraintValidatorContext.class ) ) {
      return type.cast( this );
    }
    throw LOG.getTypeNotSupportedForUnwrappingException( type );
  }
  ...
}

官方给了简单使用示例(供以参考):


    public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) {
        HibernateConstraintValidatorContext context = constraintValidatorContext.unwrap(HibernateConstraintValidatorContext.class);
        // 在addConstraintViolation之前把参数放进去,就可以创建出不同的ConstraintViolation了
        // 若不这么做,所有的ConstraintViolation取值都是一样的喽~~~
        context.addMessageParameter("foo", "bar");
        context.buildConstraintViolationWithTemplate("{foo}")
                .addConstraintViolation();
        context.addMessageParameter("foo", "snafu");
        context.buildConstraintViolationWithTemplate("{foo}")
                .addConstraintViolation();
        return false;
    }
相关文章
|
Java 编译器 API
Java 密封类:精细化控制继承关系
Java 密封类:精细化控制继承关系
544 83
|
IDE Java 数据挖掘
Java 基础类从入门到精通实操指南
这份指南专注于**Java 17+**的新特性和基础类库的现代化用法,涵盖开发环境配置、数据类型增强(如文本块)、字符串与集合处理进阶、异常改进(如密封类)、IO操作及实战案例。通过具体代码示例,如CSV数据分析工具,帮助开发者掌握高效编程技巧。同时提供性能优化建议和常用第三方库推荐,适合从入门到精通的Java学习者。资源链接:[点此下载](https://pan.quark.cn/s/14fcf913bae6)。
684 37
|
10月前
|
安全 Java 数据建模
Java记录类:简化数据载体的新选择
Java记录类:简化数据载体的新选择
524 101
|
10月前
|
安全 Java 开发者
Java记录类:简化数据载体的新方式
Java记录类:简化数据载体的新方式
396 100
|
11月前
|
安全 IDE Java
Java记录类型(Record):简化数据载体类
Java记录类型(Record):简化数据载体类
666 143
|
9月前
|
存储 Java 索引
用Java语言实现一个自定义的ArrayList类
自定义MyArrayList类模拟Java ArrayList核心功能,支持泛型、动态扩容(1.5倍)、增删改查及越界检查,底层用Object数组实现,适合学习动态数组原理。
401 4
|
9月前
|
IDE JavaScript Java
在Java 11中,如何处理被弃用的类或接口?
在Java 11中,如何处理被弃用的类或接口?
409 5
|
9月前
|
编解码 Java 开发者
Java String类的关键方法总结
以上总结了Java `String` 类最常见和重要功能性方法。每种操作都对应着日常编程任务,并且理解每种操作如何影响及处理 `Strings` 对于任何使用 Java 的开发者来说都至关重要。
505 5
|
9月前
|
JSON 网络协议 安全
【Java】(10)进程与线程的关系、Tread类;讲解基本线程安全、网络编程内容;JSON序列化与反序列化
几乎所有的操作系统都支持进程的概念,进程是处于运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位一般而言,进程包含如下三个特征。独立性动态性并发性。
431 1
|
9月前
|
Java Go 开发工具
【Java】(8)正则表达式的使用与常用类分享
正则表达式定义了字符串的模式。正则表达式并不仅限于某一种语言,但是在每种语言中有细微的差别。
542 1