深入了解数据校验(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;
    }
相关文章
|
2月前
|
Java 开发者
在 Java 中,一个类可以实现多个接口吗?
这是 Java 面向对象编程的一个重要特性,它提供了极大的灵活性和扩展性。
164 57
|
16天前
|
JSON Java Apache
Java基础-常用API-Object类
继承是面向对象编程的重要特性,允许从已有类派生新类。Java采用单继承机制,默认所有类继承自Object类。Object类提供了多个常用方法,如`clone()`用于复制对象,`equals()`判断对象是否相等,`hashCode()`计算哈希码,`toString()`返回对象的字符串表示,`wait()`、`notify()`和`notifyAll()`用于线程同步,`finalize()`在对象被垃圾回收时调用。掌握这些方法有助于更好地理解和使用Java中的对象行为。
|
2月前
|
存储 缓存 安全
java 中操作字符串都有哪些类,它们之间有什么区别
Java中操作字符串的类主要有String、StringBuilder和StringBuffer。String是不可变的,每次操作都会生成新对象;StringBuilder和StringBuffer都是可变的,但StringBuilder是非线程安全的,而StringBuffer是线程安全的,因此性能略低。
67 8
|
2月前
|
存储 安全 Java
java.util的Collections类
Collections 类位于 java.util 包下,提供了许多有用的对象和方法,来简化java中集合的创建、处理和多线程管理。掌握此类将非常有助于提升开发效率和维护代码的简洁性,同时对于程序的稳定性和安全性有大有帮助。
80 17
|
2月前
|
安全 Java
Java多线程集合类
本文介绍了Java中线程安全的问题及解决方案。通过示例代码展示了使用`CopyOnWriteArrayList`、`CopyOnWriteArraySet`和`ConcurrentHashMap`来解决多线程环境下集合操作的线程安全问题。这些类通过不同的机制确保了线程安全,提高了并发性能。
|
2月前
|
存储 Java 程序员
Java基础的灵魂——Object类方法详解(社招面试不踩坑)
本文介绍了Java中`Object`类的几个重要方法,包括`toString`、`equals`、`hashCode`、`finalize`、`clone`、`getClass`、`notify`和`wait`。这些方法是面试中的常考点,掌握它们有助于理解Java对象的行为和实现多线程编程。作者通过具体示例和应用场景,详细解析了每个方法的作用和重写技巧,帮助读者更好地应对面试和技术开发。
140 4
|
2月前
|
Java 编译器 开发者
Java异常处理的最佳实践,涵盖理解异常类体系、选择合适的异常类型、提供详细异常信息、合理使用try-catch和finally语句、使用try-with-resources、记录异常信息等方面
本文探讨了Java异常处理的最佳实践,涵盖理解异常类体系、选择合适的异常类型、提供详细异常信息、合理使用try-catch和finally语句、使用try-with-resources、记录异常信息等方面,帮助开发者提高代码质量和程序的健壮性。
89 2
|
2月前
|
存储 安全 Java
如何保证 Java 类文件的安全性?
Java类文件的安全性可以通过多种方式保障,如使用数字签名验证类文件的完整性和来源,利用安全管理器和安全策略限制类文件的权限,以及通过加密技术保护类文件在传输过程中的安全。
78 4
|
2月前
|
Java 数据格式 索引
使用 Java 字节码工具检查类文件完整性的原理是什么
Java字节码工具通过解析和分析类文件的字节码,检查其结构和内容是否符合Java虚拟机规范,确保类文件的完整性和合法性,防止恶意代码或损坏的类文件影响程序运行。
58 5
|
2月前
|
Java API Maven
如何使用 Java 字节码工具检查类文件的完整性
本文介绍如何利用Java字节码工具来检测类文件的完整性和有效性,确保类文件未被篡改或损坏,适用于开发和维护阶段的代码质量控制。
127 5