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
是何其相似。它的继承数如下:
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); }
它的继承树如下
子接口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); }
它的继承树如下:
子接口: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; }