一、引言

问题:在代码编写的过程中,数据值的校验在JavaEE三层架构(展示层、业务层、数据访问层)均有涉及,各层的校验需求又是不尽相同的,因此往往会伴随着代码冗余,重复的校验逻辑出现在三层代码中。

简介:JSR-303 Bean-Validator伴随着此类问题应运而生,Hibernate-Validator就是一个遵循JSR-303规范的优秀实践。

图片截取自Hibernate Validator 官方pdf文档。

Hibernate-Validator提供了较为完善、便捷的校验方式解决了代码校验问题。 常用于提供字段的校验,比如字段非空、字段长度限制、邮箱验证等等。

二、测试实例

下面引用Hibernate-Validator源码中ValidatorTest测试类下的方法为例:

class A {@NotNullString b;@ValidC c;@ValidD d;
}
@GroupSequence({ TestGroup.class, C.class })
class C {@Pattern(regexp = "[0-9]+", groups = TestGroup.class)@Length(min = 2)String id;C(String id) {this.id = id;}
}
@Test
public void testValidate() {ValidatorFactory factory = configuration.buildValidatorFactory(); Validator validator = factory.getValidator();A testInstance = new A();testInstance.c = new C( "aaa" );Set<ConstraintViolation<SerializableClass>> constraintViolations = validator.validate( testInstance );Set<ConstraintViolation<A>> constraintViolations = validator.validateProperty( testInstance, "c.id" );Set<ConstraintViolation<A>> constraintViolations = validator.validateValue( A.class, "c.id", "aaa" );
}

由上例可以看出,Hibernate-Validator主要是将JSR-303内置的约束以及Hibernate-Validator扩展的自定义约束通过注解的形式添加到属性上,来实现对对象内属性的校验功能。下面我们将通过上述例子拆分及解析校验实现的流程及细节,本文使用Hibernate-Validator下的7.0.1.Final版本解释源码。

三. Hibernate-Validator具体实现

1. 获取Validator实例

想要对对象进行校验,首先需要获得一个Validator校验器实例,由上述代码可知

ValidatorFactory factory = configuration.buildValidatorFactory();
Validator validator = factory.getValidator();

校验器获取方法在代码实现上较为简单,主要是遵循Java内部的SPI机制实现Java提供的相关接口实现。

主要实现分为两步:

a. validatorFactory 工厂创建

public static ValidatorFactory buildDefaultValidatorFactory() {return byDefaultProvider().configure().buildValidatorFactory();
}// 构建相关配置文件
public Configuration<?> configure() {// ValidationProviderResolver 的主要作用是确定环境下可用的 ValidationProvider 列表// 其唯一实现是 jakarta.validation.Validation 下的内部类 DefaultValidationProviderResolver ValidationProviderResolver resolver = this.resolver == null ? this.getDefaultValidationProviderResolver() : this.resolver;List validationProviders;try {// ValidationProvider的作用是提供程序校验器,通过其下的buildValidatorFactory方法构建ValidatorFactory工厂// Hibernate-Validator对其提供了唯一的实现类HibernateValidator构建并返回工厂实现ValidatorFactoryImplvalidationProviders = resolver.getValidationProviders();} catch (ValidationException var6) {throw var6;} catch (RuntimeException var7) {throw new ValidationException("Unable to get available provider resolvers.", var7);}if (validationProviders.isEmpty()) {String msg = "Unable to create a Configuration, because no Jakarta Bean Validation provider could be found. Add a provider like Hibernate Validator (RI) to your classpath.";throw new NoProviderFoundException(msg);} else {try {// 从 ValidationProvider 中获取相关通用配置Configuration<?> config = ((ValidationProvider)resolver.getValidationProviders().get(0)).createGenericConfiguration(this);return config;} catch (RuntimeException var5) {throw new ValidationException("Unable to instantiate Configuration.", var5);}}
}// 根据配置文件构建用于创建Validator的工厂并初始化相关参数
public final ValidatorFactory buildValidatorFactory() {loadValueExtractorsFromServiceLoader();// 解析Validation.xml,如何存在则解析其配置parseValidationXml();for ( ValueExtractorDescriptor valueExtractorDescriptor : valueExtractorDescriptors.values() ) {validationBootstrapParameters.addValueExtractorDescriptor( valueExtractorDescriptor );}ValidatorFactory factory = null;try {// 判断是否指定了Validator,指定了Validator则直接返回Factoryif ( isSpecificProvider() ) {factory = validationBootstrapParameters.getProvider().buildValidatorFactory( this );}else {// 判断是否指定了 ValidatorProviderfinal Class<? extends ValidationProvider<?>> providerClass = validationBootstrapParameters.getProviderClass();if ( providerClass != null ) {for ( ValidationProvider<?> provider : providerResolver.getValidationProviders() ) {if ( providerClass.isAssignableFrom( provider.getClass() ) ) {factory = provider.buildValidatorFactory( this );break;}}if ( factory == null ) {throw LOG.getUnableToFindProviderException( providerClass );}}else {List<ValidationProvider<?>> providers = providerResolver.getValidationProviders();assert providers.size() != 0; // I run therefore I am// 构建 ValidatorFactory 时初始化了很多对象,包括方法校验配置、分组排序、注解到注解校验实现的映射等factory = providers.get( 0 ).buildValidatorFactory( this );}}}finally {// close all input streams opened by this configurationfor ( InputStream in : configurationStreams ) {try {in.close();}catch (IOException io) {LOG.unableToCloseInputStream();}}}return factory;
}

b. 获取Validator实例

@Override
// getValidator 是 ValidatorFactoryImpl 下最重要的方法,用于获取 Validator 实例
public Validator getValidator() {return createValidator(constraintCreationContext.getConstraintValidatorManager().getDefaultConstraintValidatorFactory(),constraintCreationContext,validatorFactoryScopedContext,methodValidationConfiguration);
}

2. Validator校验

由实例中列举的validator校验方法可知,Validator校验分为三种类型,分别是:

  1. validate:针对所有属性进行校验;
  2. validateProperty:针对某一个具体属性进行校验;
  3. validateValue:针对某一个具体属性及特定值进行校验。

本文选用validate的相关校验流程及源码进行解析,源码功能使用代码注释的方式解释。

validate具体校验流程如下所示:

  1. 执行ValidatorImpl下的validate方法,封装validationContext(校验器上下文)、valueContext(校验对象上下文)及validationOrder(校验群组及排序信息)数据内容,方便后续调用。
// 校验对象下的所有约束是否正常
@Override
public final <T> Set<ConstraintViolation<T>> validate(T object, Class<?>... groups) {// 校验对象是否为空Contracts.assertNotNull(object, MESSAGES.validatedObjectMustNotBeNull());// 校验Class Group是否为空sanityCheckGroups(groups);@SuppressWarnings("unchecked")// 获取对象的ClassClass<T> rootBeanClass = (Class<T>) object.getClass();// 通过反射获取对象内所有Field、Method、Class相关的元数据信息BeanMetaData<T> rootBeanMetaData = beanMetaDataManager.getBeanMetaData(rootBeanClass);// 判断是否含有约束,无约束则直接返回if (!rootBeanMetaData.hasConstraints()) {return Collections.emptySet();}// 将对象元数据和实际对象内容封装成一个完成的ValidationContextBaseBeanValidationContext<T> validationContext = getValidationContextBuilder().forValidate(rootBeanClass,rootBeanMetaData, object);// 接口分组排序,分组排序决定了哪个Group先校验,哪个Group后校验,用于处理有先后关系的校验ValidationOrder validationOrder = determineGroupValidationOrder(groups);// 填充校验对象上下文信息BeanValueContext<?, Object> valueContext = ValueContexts.getLocalExecutionContextForBean(validatorScopedContext.getParameterNameProvider(), object, validationContext.getRootBeanMetaData(),PathImpl.createRootPath());// 根据校验文件必要信息进行校验操作return validateInContext(validationContext, valueContext, validationOrder);
}
  1. 执行ValidatorImpl下的validateInContext方法,根据传入对象的群组、级联信息分别进行校验
private <T, U> Set<ConstraintViolation<T>> validateInContext(BaseBeanValidationContext<T> validationContext,BeanValueContext<U, Object> valueContext, ValidationOrder validationOrder) {// 如果Bean为null则直接返回空if (valueContext.getCurrentBean() == null) {return Collections.emptySet();}// 取出当前Bean元数据BeanMetaData<U> beanMetaData = valueContext.getCurrentBeanMetaData();if (beanMetaData.isDefaultGroupSequenceRedefined()) {validationOrder.assertDefaultGroupSequenceIsExpandable(beanMetaData.getDefaultGroupSequence(valueContext.getCurrentBean()));}// 校验当前对象内的约束内容Iterator<Group> groupIterator = validationOrder.getGroupIterator();while (groupIterator.hasNext()) {Group group = groupIterator.next();valueContext.setCurrentGroup(group.getDefiningClass());// 校验当前对象validateConstraintsForCurrentGroup(validationContext, valueContext);// 判断约束校验是否失败if (shouldFailFast(validationContext)) {return validationContext.getFailingConstraints();}}// 级联校验,即校验一个对象内可能存在的需要校验的其他对象groupIterator = validationOrder.getGroupIterator();while (groupIterator.hasNext()) {Group group = groupIterator.next();valueContext.setCurrentGroup(group.getDefiningClass());// 校验级联对象validateCascadedConstraints(validationContext, valueContext);// 判断约束校验是否失败if (shouldFailFast(validationContext)) {return validationContext.getFailingConstraints();}}// 如果需要校验的对象包含GroupSequece,按次序校验则走以下方法Iterator<Sequence> sequenceIterator = validationOrder.getSequenceIterator();while (sequenceIterator.hasNext()) {Sequence sequence = sequenceIterator.next();for (GroupWithInheritance groupOfGroups : sequence) {int numberOfViolations = validationContext.getFailingConstraints().size();for (Group group : groupOfGroups) {valueContext.setCurrentGroup(group.getDefiningClass());// 校验对象validateConstraintsForCurrentGroup(validationContext, valueContext);// 判断约束校验是否失败if (shouldFailFast(validationContext)) {return validationContext.getFailingConstraints();}// 校验级联对象validateCascadedConstraints(validationContext, valueContext);// 判断约束校验是否失败if (shouldFailFast(validationContext)) {return validationContext.getFailingConstraints();}}if (validationContext.getFailingConstraints().size() > numberOfViolations) {break;}}}return validationContext.getFailingConstraints();
}
  1. 根据校验属性的Group执行相应校验方法
private void validateConstraintsForCurrentGroup(BaseBeanValidationContext<?> validationContext,BeanValueContext<?, Object> valueContext) {if (!valueContext.validatingDefault()) {// 如果校验的group非Default Group,走该校验方法validateConstraintsForNonDefaultGroup(validationContext, valueContext);}else {// 如果校验的group为Default Group,走该校验方法validateConstraintsForDefaultGroup(validationContext, valueContext);}
}
  1. 解析当前对象的继承结构,根据继承结构和默认的GroupSequence进行校验
// 默认约束校验
private <U> void validateConstraintsForDefaultGroup(BaseBeanValidationContext<?> validationContext,BeanValueContext<U, Object> valueContext) {// 获取currentBeanMetaData对象校验当前对象内容final BeanMetaData<U> beanMetaData = valueContext.getCurrentBeanMetaData();final Map<Class<?>, Class<?>> validatedInterfaces = new HashMap<>();// 评估类的继承结构中每个类的约束,依次校验该类及其父类约束for (Class<? super U> clazz : beanMetaData.getClassHierarchy()) {BeanMetaData<? super U> hostingBeanMetaData = beanMetaDataManager.getBeanMetaData(clazz);boolean defaultGroupSequenceIsRedefined = hostingBeanMetaData.isDefaultGroupSequenceRedefined();// 如果默认的GroupSequence被重新定义,说明需要对属性校验排序处理,则走以下方法进行校验if (defaultGroupSequenceIsRedefined) {Iterator<Sequence> defaultGroupSequence = hostingBeanMetaData.getDefaultValidationSequence(valueContext.getCurrentBean());Set<MetaConstraint<?>> metaConstraints = hostingBeanMetaData.getMetaConstraints();while (defaultGroupSequence.hasNext()) {// 区分GroupSequence进行校验for (GroupWithInheritance groupOfGroups : defaultGroupSequence.next()) {boolean validationSuccessful = true;for (Group defaultSequenceMember : groupOfGroups) {// 遍历GroupSequence下的Group进行校验validationSuccessful = validateConstraintsForSingleDefaultGroupElement(validationContext,valueContext, validatedInterfaces, clazz, metaConstraints, defaultSequenceMember)&& validationSuccessful;}validationContext.markCurrentBeanAsProcessed(valueContext);if (!validationSuccessful) {break;}}}}// fast path in case the default group sequence hasn't been redefinedelse {// 查询当前Bean下可以直接进行校验的属性Set<MetaConstraint<?>> metaConstraints = hostingBeanMetaData.getDirectMetaConstraints();// 执行校验validateConstraintsForSingleDefaultGroupElement(validationContext, valueContext, validatedInterfaces,clazz, metaConstraints, Group.DEFAULT_GROUP);validationContext.markCurrentBeanAsProcessed(valueContext);}// 层级中的所有约束均完成校验,则停止校验if (defaultGroupSequenceIsRedefined) {break;}}
}
  1. 循环校验对象的每个属性
private <U> boolean validateConstraintsForSingleDefaultGroupElement(BaseBeanValidationContext<?> validationContext,ValueContext<U, Object> valueContext, final Map<Class<?>, Class<?>> validatedInterfaces, Class<? super U> clazz,Set<MetaConstraint<?>> metaConstraints, Group defaultSequenceMember) {boolean validationSuccessful = true;valueContext.setCurrentGroup(defaultSequenceMember.getDefiningClass());// 循环校验对象的每个属性for (MetaConstraint<?> metaConstraint : metaConstraints) {// HV-466, an interface implemented more than one time in the hierarchy has to be validated only one// time. An interface can define more than one constraint, we have to check the class we are validating.final Class<?> declaringClass = metaConstraint.getLocation().getDeclaringClass();if (declaringClass.isInterface()) {Class<?> validatedForClass = validatedInterfaces.get(declaringClass);if (validatedForClass != null && !validatedForClass.equals(clazz)) {continue;}validatedInterfaces.put(declaringClass, clazz);}// 校验类非interface接口则正常校验boolean tmp = validateMetaConstraint(validationContext, valueContext, valueContext.getCurrentBean(),metaConstraint);// 快速失败if (shouldFailFast(validationContext)) {return false;}validationSuccessful = validationSuccessful && tmp;}return validationSuccessful;
}
  1. 判断对象是否满足校验条件并对对象进行校验
private boolean validateMetaConstraint(BaseBeanValidationContext<?> validationContext,ValueContext<?, Object> valueContext, Object parent, MetaConstraint<?> metaConstraint) {BeanValueContext.ValueState<Object> originalValueState = valueContext.getCurrentValueState();valueContext.appendNode(metaConstraint.getLocation());boolean success = true;// 判断待校验文件是否满足校验条件,如对象属性分组数及校验数是否匹配,校验分组是否与当前分组匹配等if (isValidationRequired(validationContext, valueContext, metaConstraint)) {if (parent != null) {valueContext.setCurrentValidatedValue(valueContext.getValue(parent, metaConstraint.getLocation()));}// 对象属性校验success = metaConstraint.validateConstraint(validationContext, valueContext);validationContext.markConstraintProcessed(valueContext.getCurrentBean(), valueContext.getPropertyPath(),metaConstraint);}// 对象属性校验完成后重置校验数据valueContext.resetValueState(originalValueState);return success;
}
  1. 具体对象值提取校验逻辑
public boolean validateConstraint(ValidationContext<?> validationContext, ValueContext<?, Object> valueContext) {boolean success = true;// 提取集合内容器循环校验if (valueExtractionPath != null) {Object valueToValidate = valueContext.getCurrentValidatedValue();if (valueToValidate != null) {TypeParameterValueReceiver receiver = new TypeParameterValueReceiver(validationContext, valueContext,valueExtractionPath);ValueExtractorHelper.extractValues(valueExtractionPath.getValueExtractorDescriptor(), valueToValidate,receiver);success = receiver.isSuccess();}}// 常规校验流程else {success = doValidateConstraint(validationContext, valueContext);}return success;
}
  1. 失败异常处理
public final boolean validateConstraints(ValidationContext<?> validationContext, ValueContext<?, ?> valueContext) {List<ConstraintValidatorContextImpl> violatedConstraintValidatorContexts = new ArrayList<>(5);// 属性值执行校验validateConstraints(validationContext, valueContext, violatedConstraintValidatorContexts);// 如果violatedConstraintValidatorContexts集合不为空,则说明校验失败,校验失败属性处理if (!violatedConstraintValidatorContexts.isEmpty()) {for (ConstraintValidatorContextImpl constraintValidatorContext : violatedConstraintValidatorContexts) {for (ConstraintViolationCreationContext constraintViolationCreationContext : constraintValidatorContext.getConstraintViolationCreationContexts()) {validationContext.addConstraintFailure(valueContext, constraintViolationCreationContext,constraintValidatorContext.getConstraintDescriptor());}}return false;}return true;
}
  1. 获取属性校验对应校验器及构建约束校验器上下文
protected void validateConstraints(ValidationContext<?> validationContext, ValueContext<?, ?> valueContext,Collection<ConstraintValidatorContextImpl> violatedConstraintValidatorContexts) {if (LOG.isTraceEnabled()) {LOG.tracef("Validating value %s against constraint defined by %s.", valueContext.getCurrentValidatedValue(),descriptor);}// 获取对象相关的Validator校验器,注解与校验器的映射在ValidatorImpl初始化时就已经取出,存在校验上下文对象中。// 通过AbstractConstraintValidatorManagerImpl下的findMatchingValidatorDescriptor查询并匹配到对应属性。ConstraintValidator<B, ?> validator = getInitializedConstraintValidator(validationContext, valueContext);// 构建约束校验器上下文ConstraintValidatorContextImpl constraintValidatorContext = validationContext.createConstraintValidatorContextFor(descriptor, valueContext.getPropertyPath());// 校验,如果校验失败则加入集合violatedConstraintValidatorContextsif (validateSingleConstraint(valueContext, constraintValidatorContext, validator).isPresent()) {violatedConstraintValidatorContexts.add(constraintValidatorContext);}
}
  1. 通过筛选出的validator完成校验
protected final <V> Optional<ConstraintValidatorContextImpl> validateSingleConstraint(ValueContext<?, ?> valueContext, ConstraintValidatorContextImpl constraintValidatorContext,ConstraintValidator<A, V> validator) {boolean isValid;try {// 获取当前要校验的对象值value@SuppressWarnings("unchecked")V validatedValue = (V) valueContext.getCurrentValidatedValue();// 使用注解和属性类型对应的Validator进行校验isValid = validator.isValid(validatedValue, constraintValidatorContext);}catch (RuntimeException e) {if (e instanceof ConstraintDeclarationException) {throw e;}throw LOG.getExceptionDuringIsValidCallException(e);}if (!isValid) {// We do not add these violations yet, since we don't know how they are// going to influence the final boolean evaluationreturn Optional.of(constraintValidatorContext);}return Optional.empty();
}

上述代码即是从ValidatorImpl下的validator方法开始到实现注解属性校验的基础校验过程源码解析。
validateProperty和validateValue两种方法与validate方法差异不大,此处不进行过多赘述。

当校验对象中存在嵌套对象需要校验时,此时需要使用@Valid注解标注出需要校验的对象,通过级联校验功能进行校验。

private <T, U> Set<ConstraintViolation<T>> validateInContext(BaseBeanValidationContext<T> validationContext,BeanValueContext<U, Object> valueContext, ValidationOrder validationOrder) {// 级联校验,即校验一个对象内可能存在的需要校验的其他对象groupIterator = validationOrder.getGroupIterator();while (groupIterator.hasNext()) {Group group = groupIterator.next();valueContext.setCurrentGroup(group.getDefiningClass());// 校验级联对象validateCascadedConstraints(validationContext, valueContext);// 判断约束校验是否失败if (shouldFailFast(validationContext)) {return validationContext.getFailingConstraints();}}return validationContext.getFailingConstraints();
}
private void validateCascadedConstraints(BaseBeanValidationContext<?> validationContext,ValueContext<?, Object> valueContext) {// 获取当前对象下的可校验内容Validatable validatable = valueContext.getCurrentValidatable();BeanValueContext.ValueState<Object> originalValueState = valueContext.getCurrentValueState();// 校验当前对象下注释了@Valid的属性或方法参数for (Cascadable cascadable : validatable.getCascadables()) {valueContext.appendNode(cascadable);// 确认级联校验对象处于可校验状态if (isCascadeRequired(validationContext, valueContext.getCurrentBean(), valueContext.getPropertyPath(),cascadable.getConstraintLocationKind())) {Object value = getCascadableValue(validationContext, valueContext.getCurrentBean(), cascadable);CascadingMetaData cascadingMetaData = cascadable.getCascadingMetaData();if (value != null) {CascadingMetaData effectiveCascadingMetaData = cascadingMetaData.addRuntimeContainerSupport(valueExtractorManager, value.getClass());// 校验被@Valid注释的级联属性,递归validateInContext方法实现递归级联校验if (effectiveCascadingMetaData.isCascading()) {validateCascadedAnnotatedObjectForCurrentGroup(value, validationContext, valueContext,effectiveCascadingMetaData);}// Container集合数据特殊处理if (effectiveCascadingMetaData.isContainer()) {ContainerCascadingMetaData containerCascadingMetaData = effectiveCascadingMetaData.as(ContainerCascadingMetaData.class);if (containerCascadingMetaData.hasContainerElementsMarkedForCascading()) {// 校验Container内元素约束validateCascadedContainerElementsForCurrentGroup(value, validationContext, valueContext,containerCascadingMetaData.getContainerElementTypesCascadingMetaData());}}}}// 重置valueContext值内容valueContext.resetValueState(originalValueState);}
}
private void validateCascadedAnnotatedObjectForCurrentGroup(Object value,BaseBeanValidationContext<?> validationContext, ValueContext<?, Object> valueContext,CascadingMetaData cascadingMetaData) {Class<?> originalGroup = valueContext.getCurrentGroup();Class<?> currentGroup = cascadingMetaData.convertGroup(originalGroup);// 判断数据是否已经校验过或者校验是否已经失败if (validationContext.isBeanAlreadyValidated(value, currentGroup, valueContext.getPropertyPath())|| shouldFailFast(validationContext)) {return;}ValidationOrder validationOrder = validationOrderGenerator.getValidationOrder(currentGroup,currentGroup != originalGroup);BeanValueContext<?, Object> cascadedValueContext = buildNewLocalExecutionContext(valueContext, value);// 递归校验validateInContext(validationContext, cascadedValueContext, validationOrder);
}

3. Validator功能拓展

Validator除基础的校验流程外,为了强化功能以满足更多场景的需要,校验流程中还提供了诸如Group(群组)、Sequence(优先级)等功能,下面将解读这些功能在校验中的相关类源码。

  1. Group(群组)

a. 为类中的属性约束定义群组

@Data
public class Driver extends Person {@Min(value = 18, message = "You have to be 18 to drive a car", groups = DriverChecks.class)public int age;public Driver(String name) {super(name);}
}

b. 封装校验群组及校验顺序信息到ValidationOrder对象中

public ValidationOrder getValidationOrder(Collection<Class<?>> groups) {// 如果Group为空则抛出异常if ( groups == null || groups.size() == 0 ) {throw LOG.getAtLeastOneGroupHasToBeSpecifiedException();}// Group集合下仅包含默认Group则直接返回if ( groups.size() == 1 && groups.contains( Default.class ) ) {return ValidationOrder.DEFAULT_GROUP;}// 所有的group类均为接口类,如果存在非接口Group则抛出异常for ( Class<?> clazz : groups ) {if ( !clazz.isInterface() ) {throw LOG.getGroupHasToBeAnInterfaceException( clazz );}}// 根据集合groups填充validationOrder群组及排序信息DefaultValidationOrder validationOrder = new DefaultValidationOrder();for ( Class<?> clazz : groups ) {if ( Default.class.equals( clazz ) ) { validationOrder.insertGroup( Group.DEFAULT_GROUP );}else if ( isGroupSequence( clazz ) ) {insertSequence( clazz, clazz.getAnnotation( GroupSequence.class ).value(), true, validationOrder );}else {Group group = new Group( clazz );validationOrder.insertGroup( group );insertInheritedGroups( clazz, validationOrder );}}return validationOrder;
}

c. validateInContext方法通过遍历ValidationOrder对象中的分组信息和序列信息进行校验

// 校验当前对象内的约束内容
Iterator<Group> groupIterator = validationOrder.getGroupIterator();
while (groupIterator.hasNext()) {…………
}
// 如果需要校验的对象包含GroupSequece,按次序校验则走以下方法
Iterator<Sequence> sequenceIterator = validationOrder.getSequenceIterator();
while (sequenceIterator.hasNext()) {…………
}

d. 后续指定Group后直接执行循环校验,校验分组与指定分组相同的属性。

private boolean isValidationRequired(BaseBeanValidationContext<?> validationContext,ValueContext<?, ?> valueContext, MetaConstraint<?> metaConstraint) {if (!validationContext.appliesTo(metaConstraint)) {return false;}if (validationContext.hasMetaConstraintBeenProcessed(valueContext.getCurrentBean(),valueContext.getPropertyPath(), metaConstraint)) {return false;}// 分组校验,如果需要校验的属性分组与当前正在校验分组不同,则不进行校验if (!metaConstraint.getGroupList().contains(valueContext.getCurrentGroup())) {return false;}return isReachable(validationContext, valueContext.getCurrentBean(), valueContext.getPropertyPath(),metaConstraint.getConstraintLocationKind());
}

分组校验通过对Validator注解增加分组的方式,限定了校验需要生效的范围,在调用validator方法时指定本次校验的分组,即可指定哪些方法需要校验。

  1. Sequence(序列)

用户可以通过GroupSequence的方式指定分组的优先级,高优先级的分组可以优先执行校验逻辑。

private void insertSequence(Class<?> sequenceClass, Class<?>[] sequenceElements, boolean cache, DefaultValidationOrder validationOrder) {Sequence sequence = cache ? resolvedSequences.get( sequenceClass ) : null;if ( sequence == null ) {// 解析校验对象的GroupSequence校验优先级数据sequence = resolveSequence( sequenceClass, sequenceElements, new ArrayList<Class<?>>() );// 如果存在分组继承关系,则检查分组继承关系新增Groupsequence.expandInheritedGroups();// 缓存校验优先级信息if ( cache ) {final Sequence cachedResolvedSequence = resolvedSequences.putIfAbsent( sequenceClass, sequence );if ( cachedResolvedSequence != null ) {sequence = cachedResolvedSequence;}}}validationOrder.insertSequence( sequence );
}

4. 自定义约束

除了Hibernate-Validator提供的约束外,用户在开发过程中经常需要基于自身业务需求自定义约束。

基于Hibernate-Validator框架拓展validator约束功能较为简单,仅需实现其自定义约束的两个部分,注解及其注解的约束实现。

注解是否为约束注解的解析实现在BeanMetaData对象的构造过程中,具体实现在org.hibernate.validator.internal.metadata.provider.AnnotationMetaDataProvider类下的findConstraintAnnotations方法中:

protected <A extends Annotation> List<ConstraintDescriptorImpl<?>> findConstraintAnnotations(Member member,A annotation,ElementType type) {if ( annotation.annotationType().getPackage().getName().equals( "jdk.internal" ) ) {return Collections.emptyList();}List<ConstraintDescriptorImpl<?>> constraintDescriptors = newArrayList();List<Annotation> constraints = newArrayList();Class<? extends Annotation> annotationType = annotation.annotationType();if ( constraintHelper.isConstraintAnnotation( annotationType ) ) {constraints.add( annotation );}else if ( constraintHelper.isMultiValueConstraint( annotationType ) ) {constraints.addAll( constraintHelper.getConstraintsFromMultiValueConstraint( annotation ) );}for ( Annotation constraint : constraints ) {final ConstraintDescriptorImpl<?> constraintDescriptor = buildConstraintDescriptor(member, constraint, type);constraintDescriptors.add( constraintDescriptor );}return constraintDescriptors;
}

保证注解满足constraintHelper.isConstraintAnnotation方法中的要求即可,包括其中必要参数及注解的校验。

public boolean isConstraintAnnotation(Class<? extends Annotation> annotationType) {// 查询是否是Hibernate-Validator自身约束,是则直接返回trueif (isBuiltinConstraint(annotationType)) {return true;}// 自定义约束检查注解及参数是否合法if (annotationType.getAnnotation(Constraint.class) == null) {return false;}return externalConstraints.computeIfAbsent(annotationType, a -> {assertMessageParameterExists(a);assertGroupsParameterExists(a);assertPayloadParameterExists(a);assertValidationAppliesToParameterSetUpCorrectly(a);assertNoParameterStartsWithValid(a);return Boolean.TRUE;});
}

约束的实现类则可直接在注解类中通过 @Constraint(validatedBy = {……}) 指定,本文在此不进行过多赘述。

5. 结语

Hibernate-Validator是一种较为优雅的数据校验实现方式,在日常的工作中已经得到了广泛的应用,希望本文能够帮助使用者更好更完善的从底层源码的角度了解Hibernate-Validator的功能,如果本文存在错误或遗漏请与作者联系,谢谢

Hibernate Validator源码解析相关推荐

  1. springboot源码解析autoconfigure之WebMvcAutoConfiguration

    2019独角兽企业重金招聘Python工程师标准>>> 说在前面 本次开始spring-boot-autoconfigure源码解析之WebMvcAutoConfiguration ...

  2. clickhouse原理解析与开发实战 pdf_重识SSM,“超高频面试点+源码解析+实战PDF”,一次性干掉全拿走...

    重识SSM,"超高频面试点"+"源码解析"+"实战PDF",一次性干掉全拿走!! 01 超高频面试点知识篇 1.1 Spring超高频面试点 ...

  3. php的lumen框架,Lumen框架“服务容器”源码解析

    1.服务容器 "服务容器"是Lumen框架整个系统功能调度配置的核心,它提供了整个框架运行过程中的一系列服务."服务容器"就是提供服务(服务可以理解为系统运行中 ...

  4. SpringDataJPA+Hibernate框架源码剖析(六)@PersistenceContext和@Autowired注入EntityManager的区别

    SpringDataJPA+Hibernate框架源码剖析系列文章: SpringDataJPA+Hibernate框架源码剖析(一)框架介绍 SpringDataJPA+Hibernate框架源码剖 ...

  5. Tomcat8源码解析

    Tomcat8源码解析 Tomcat总体架构 Connector:开启Socket并监听客户端请求,返回响应数据: Container:负责具体的请求处理: 一个Service负责维护多个Connec ...

  6. Spring源码深度解析(郝佳)-Spring 常用注解使用及源码解析

      我们在看Spring Boot源码时,经常会看到一些配置类中使用了注解,本身配置类的逻辑就比较复杂了,再加上一些注解在里面,让我们阅读源码更加难解释了,因此,这篇博客主要对配置类上的一些注解的使用 ...

  7. Spring源码深度解析(郝佳)-学习-源码解析-Spring MVC(三)-Controller 解析

    在之前的博客中Spring源码深度解析(郝佳)-学习-源码解析-Spring MVC(一),己经对 Spring MVC 的框架做了详细的分析,但是有一个问题,发现举的例子不常用,因为我们在实际开发项 ...

  8. 01.Fabric源码解析---线头(王雅震)

    Fabric源码解析1--线头 Getting Started 简单提一下Fabric说明文档中的Getting Started部分.说明文档下载地址在 http://hyperledger-fabr ...

  9. 谷歌BERT预训练源码解析(二):模型构建

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/weixin_39470744/arti ...

最新文章

  1. 《机器学习实战》chapter05 Logistic回归
  2. LightGBM如何保存模型?
  3. 使用Spring Cloud HystrixCommands的功能Hystrix
  4. 会聊天到底有多重要?汽车语音识别大盘点
  5. python转移矩阵_使用Python / Numpy中的单词构建转换矩阵
  6. 把wasm反编译出来
  7. ORACLE之常用FAQ V1.0二(构架系统) (1)
  8. Java I/O 全面详解
  9. 指数分布的极大似然估计
  10. Codeforces1221 C. Perfect Team
  11. 设计模式回顾——模板模式(C++)
  12. Macbook Pro 外接显示器后,鼠标滑动延迟
  13. 后台接口统一返回类型-ResponseBodyAdvice
  14. Java调试--排查类工具
  15. linux下c语言调用mysql,Linux下C语言操作MYSQL总结
  16. 【C语言程序设计】实验 1
  17. 【来日复制粘贴】关于排名
  18. 不同波段成像(数字图像)
  19. 杂乱身份证整理之终极大法
  20. html雪花飘落计划书,5.html5 作业 canvas 雪花飘落

热门文章

  1. python逗号表达式_python自增自减?赋值语句返回值?逗号表达式?
  2. HelloKitty勒索软件增加了DDoS攻击、十大常见的网络钓鱼邮件主题|11月2日全球网络安全热点
  3. 计算球堆积密度的c语言程序,用于形成包含堆积在任意形状的体积中的多分散球形颗粒的计算机化模型的方法和系统与流程...
  4. Linux SIGABRT和abort()函数
  5. 热烈祝贺|济南市时代酒具盛装亮相2023中国(山东)精酿啤酒产业发展创新论坛暨展览会
  6. HTML点击链接打开新的标签页
  7. 清除SVN未版控文件
  8. Xshell远程连接linux系统失败
  9. 云丁科技获6亿D轮融资:百度领投 顺为SIG险峰跟投
  10. Eclipse中jsp异常 The superclass