Hibernate Validator源码解析

article/2025/10/6 20:34:22

一、引言

问题:在代码编写的过程中,数据值的校验在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的功能,如果本文存在错误或遗漏请与作者联系,谢谢


http://chatgpt.dhexx.cn/article/NscUseDi.shtml

相关文章

Hibernate Validator简介

亲爱的小伙伴们我来填坑啦&#xff0c;java中优雅的参数校验方法中的校验的实现原理。 1.前言 验证数据是发生在所有应用程序层&#xff08;从表示层到持久层&#xff09;的常见任务。通常在每一层中实现相同的验证逻辑&#xff0c;这既耗时又容易出错。为了避免重复这些验证&…

bootstrapValidator验证最大值最小值范围限制

录入该值的最大值与最小值 bootstrapValidator进行效验&#xff0c;使最小值不可大于最大值&#xff0c;最大值不可小于最小值 刚开始的验证还是没事的&#xff0c;符合正常的验证规则 再把不符合规则的最大值改变&#xff0c;现在最小值已经比最大值小了&#xff0c;但是最大…

class-validator中文教程

官方文档&#xff1a; https://www.npmjs.com/package/class-validator class-validator可以说是一个简化验证的依赖库 &#xff08;采用注释的方式进行校验&#xff09; 但是缺少中文文档和过程&#xff0c;以自己的理解和对官网文档的阅读进行整理输出。 它的好兄弟class-t…

Hibernate Validator 总结大全

背景 代码开发过程中&#xff0c;参数的有效性校验是一项很繁琐的工作&#xff0c; 如果参数简单&#xff0c;就那么几个参数&#xff0c;直接通过ifelse可以搞定&#xff0c;如果参数太多&#xff0c;比如一个大对象有100多个字段作为入参&#xff0c;你如何校验呢&#xff1…

java使用validator进行校验

不管是html页面表单提交的对象数据还是和第三方公司进行接口对接&#xff0c;都需要对接收到的数据进行校验&#xff08;非空、长度、格式等等&#xff09;。如果使用if一个个进行校验&#xff08;字段非常多&#xff09;&#xff0c;这是让人崩溃的过程。幸好jdk或hibernate都…

java validator_Spring中校验器(Validator)的深入讲解

前言 Spring框架的 validator 组件,是个辅助组件,在进行数据的完整性和有效性非常有用,通过定义一个某个验证器,即可在其它需要的地方,使用即可,非常通用。 应用在执行业务逻辑之前,必须通过校验保证接受到的输入数据是合法正确的,但很多时候同样的校验出现了多次,在不…

springboot使用hibernate validator校验

目录 一、参数校验二、hibernate validator校验demo三、hibernate的校验模式 1、普通模式&#xff08;默认是这个模式&#xff09;2、快速失败返回模式四、hibernate的两种校验 1、请求参数校验2、GET参数校验(RequestParam参数校验)3、model校验4、对象级联校验5、分组校验五…

Validator 使用总结

介绍 首先说下大家常用的hibernate-validator&#xff0c;它是对JSR-303/JSR-349标准的实现&#xff0c;然后spring为了给开发者提供便捷集成了hibernate-validator&#xff0c;默认在springmvc模块。 依赖 本文所介绍皆在springboot应用的基础上&#xff0c;首先加上web模块…

浅谈 Android Tombstone(墓碑日志)分析步骤

最近项目产品刚刚出货&#xff0c;客户退机、死机事件频发。日常解决bug中&#xff0c;少不了和墓碑日志打交道&#xff0c;截止今天之前&#xff0c;见到墓碑日志都是一脸懵逼&#xff0c;不知道怎么分析。最近又有了两个日志&#xff0c;硬着头皮看吧。之所以称之为浅谈&…

Android tombstone文件是如何生成的

本节内容我们聚焦到androidQ上&#xff0c;分析android中一个用于debug的功能&#xff0c;那就是tombstone&#xff0c;俗称“墓碑”。现实生活中墓碑一般是给死人准备的&#xff0c;而在android系统中“墓碑”则是给进程准备的。 为何Android要设计出这样一个东西呢&#xff…

【Android NDK 开发】NDK C/C++ 代码崩溃调试 - Tombstone 报错信息日志文件分析 ( 获取 tombstone_0X 崩溃日志信息 )

文章目录 一、崩溃信息描述二、手机命令行操作三、电脑命令行操作四、Tombstone 内容 Tombstone 报错信息日志文件被保存在了 /data/tombstones/ 目录下 , 先 ROOT 再说 , 没有 ROOT 权限无法访问该目录中的信息 ; 使用 Pixel 2 手机进行调试 , 其它 ROOT 后的手机也可以使用 …

Android tombstone 分析案例

Android tombstone 分析案例 tombstone文件内容1. 体系结构2. 发生Crash线程3. 原因4. 寄存器状态4.1 处理器工作模式下的寄存器4.2 未分组寄存器r0 – r74.3 分组寄存器r8 – r144.4 程序计数器pc(r15)4.5 程序状态寄存器4.6 ARM参数规则 5. 回溯栈6. 程序栈7. 寄存器地址附近…

RocksDB Tombstone 详解

目录 为什么会有墓碑&#xff1f; 使用场景 原理 描述 分段 查询 优化点 总结 为什么会有墓碑&#xff1f; 我们知道 TP 数据库一般选择 KV 引擎作为存储引擎&#xff0c;数据库的元数据和数据通过一定的编码规则变成 KV 对存储在存储引擎中&#xff0c;比如 CockroachD…

Tombstone 文件分析

Tombstone 文件分析 /* * 下面信息是dropbox负责添加的 **/ isPrevious: true Build: Rock/odin/odin:7.1.1/NMF26F/1500868195:user/dev-keys Hardware: msm8953 Revision: 0 Bootloader: unknown Radio: unknown Kernel: Linux version 3.18.31-perf-g34cb3d1 (smartcmhardc…

android Tombstone 流程

一 总述 下面是一份dump 的log&#xff1a; 810 876 I system_server: libdebuggerd_client: started dumping process 678 740 740 I /system/bin/tombstoned: registered intercept for pid 678 and type kDebuggerdNativeBacktrace 678 678 I libc : Requested du…

android tombstone log分析

今天和大家一起聊聊android 中出现的 Tombstone问题&#xff0c;近期在定制pad 上分析设备概率性重启&#xff0c;导出bugreport日志后&#xff0c;除了看到anr log外&#xff0c;同级目录下还看到了tombstones 并且对比以往日志&#xff0c;发现都生产了大量tombstone...,于是…

深入学习tombstone和signal

三驾马车&#xff08;CPU&#xff0c;内存和存储设备&#xff09;中&#xff0c;跑得最慢的就是存储设备了 电脑上&#xff0c;从HDD 到SSD&#xff0c;从SATA SSD到PCIe SSD&#xff0c;硬盘是越来越快&#xff1b; 手机上&#xff0c;从SD卡&#xff0c;到eMMC卡&#xff0…

tombstone

1.什么是tombstone 当一个动态库&#xff08;native 程序&#xff09;开始执行时&#xff0c;系统会注册一些连接到 debuggerd 的 signal handlers&#xff0c;当系统 crash 的时候&#xff0c;会保存一个 tombstone 文件到/data/tombstones目录下&#xff08;Logcat中也会有相…

Tombstone原理分析

本文主要围绕三个问题对tombstone进行分析和介绍&#xff0c;debuggerd是如何监控进程并生成tombstone的&#xff1f;tombstone文件中的信息都是什么&#xff0c;是怎么获取的&#xff1f;tombstone文件应该怎么分析&#xff1f; 一、Tombstone简介 当一个native程序开始执行时…

【date】Linux date命令修改时间的问题

Linux date命令修改时间的问题 问题路径找原因解决方法 问题 Android10&#xff1b;高通平台 使用下面date命令修改时间日期&#xff0c;时分秒生效&#xff0c;年月日不生效 > date -D YYYY-MM-DD hh:mm:ss 路径 \android\external\toybox\toys\posix\date.c \android\e…