【源码】Spring validation参数校验之分组序列校验@GroupSequenceProvider、@GroupSequence的实现原理

Spring validation参数校验系列

1、Spring validation参数校验基本使用

2、Spring validation参数校验之自定义校验规则及编程式校验等进阶篇

3、Spring validation参数校验原理解析之Controller控制器参数校验中@RequestBody参数校验实现原理

4、Spring validation参数校验原理解析之Controller控制器参数校验中@ModelAttribute及实体类参数校验实现原理

5、Spring validation参数校验原理解析之基本类型参数及Service层方法参数校验实现原理

6、Spring validation校验的核心类ValidatorImpl、MetaDataProvider和AnnotationMetaDataProvider源码分析

7、Spring validation参数校验高级篇之跨参数校验Cross-Parameter及分组序列校验@GroupSequenceProvider、@GroupSequence

8、【源码】Spring validation参数校验之跨参数校验Cross-Parameter原理分析

9、【源码】Spring validation参数校验之分组序列校验@GroupSequenceProvider、@GroupSequence的实现原理

前言

本篇继续从源码的角度分析一下Spring validation参数校验的分组序列校验@GroupSequenceProvider、@GroupSequence的实现原理。

温馨提醒:

Hibernate validation的设计比较复杂,要一次性全部分析清楚很困难,关联的细节很多。所以《Spring validation参数校验系列》文章通过从整体到细节,在每一篇中,不影响主题内容的情况下,穿插引入一些细节。在分享中,也会暂时忽略一些细节,留在下一篇讲解。建议如果本篇不太理解的,可以看看该系列的上一篇或者下一篇源码讲解文章。

一、解析分组序列

类中添加的约束元数据信息首次解析是在BeanMetaDataManagerImpl的createBeanMetaData()方法中调用。首次解析调用流程详见

【源码】Spring validation参数校验原理解析之Controller控制器参数校验中@RequestBody参数校验实现原理_@requestbody 校验字段-CSDN博客

的ValidatorImpl.validate()部分。入口方法为BeanMetaDataManagerImpl的createBeanMetaData()方法。

    private <T> BeanMetaDataImpl<T> createBeanMetaData(Class<T> clazz) {BeanMetaDataBuilder<T> builder = BeanMetaDataBuilder.getInstance(constraintCreationContext, executableHelper, parameterNameProvider,validationOrderGenerator, clazz, methodValidationConfiguration );for ( MetaDataProvider provider : metaDataProviders ) {// getBeanConfigurationForHierarchy()方法遍历beanClass及其父类,调用AnnotaionMetaDataProvider.getBeanConfiguration()方法// 获取对应类添加的约束注解,封装成BeanConfiguration对象for ( BeanConfiguration<? super T> beanConfiguration : getBeanConfigurationForHierarchy( provider, clazz ) ) {// 在BeanMetaDataBuilder中添加BeanConfiguration对象// BeanMetaDataBuilder.add()【获取并遍历约束元素,获取beanConfiguration中的sequenceSource// 和defaultGroupSequence保存到builder中builder.add( beanConfiguration );}}// 将类中添加的约束信息封装成BeanMetaDataImpl对象。return builder.build();}

1.1 AnnotationMetaDataProvider的getBeanConfiguration()方法解析类中的方法、属性、构造器添加的约束注解。

public class AnnotationMetaDataProvider implements MetaDataProvider {/*** 获取bean类中定义的属性、类、参数、方法添加的约束信息、配置来源、分组序列以及动态分组序列程序*/@Override@SuppressWarnings("unchecked")public <T> BeanConfiguration<T> getBeanConfiguration(Class<T> beanClass) {if ( Object.class.equals( beanClass ) ) {return (BeanConfiguration<T>) objectBeanConfiguration;}return retrieveBeanConfiguration( beanClass );}/*** 检索bean类的元数据信息*/private <T> BeanConfiguration<T> retrieveBeanConfiguration(Class<T> beanClass) {// 获取类中定义的属性添加的约束元素信息Set<ConstrainedElement> constrainedElements = getFieldMetaData( beanClass );// 获取类中定义的方法添加的约束元素信息constrainedElements.addAll( getMethodMetaData( beanClass ) );// 获取类的构造方法添加的约束元素信息constrainedElements.addAll( getConstructorMetaData( beanClass ) );// 获取类级添加的约束信息Set<MetaConstraint<?>> classLevelConstraints = getClassLevelConstraints( beanClass );if ( !classLevelConstraints.isEmpty() ) {ConstrainedType classLevelMetaData =new ConstrainedType(ConfigurationSource.ANNOTATION,beanClass,classLevelConstraints);constrainedElements.add( classLevelMetaData );}// 封装成BeanConfiguration对象,来源为Annotationreturn new BeanConfiguration<>(ConfigurationSource.ANNOTATION,beanClass,constrainedElements,// 获取bean中添加的@GroupSequence注解的组序列。getDefaultGroupSequence( beanClass ),// 获取类中定义的动态分组的约定程序getDefaultGroupSequenceProvider( beanClass ));}/*** 获取bean中添加的@GroupSequence注解的组序列。组序列中的组顺序执行,排前面的组对应的注解约束会先判断,如果前面的失败了,不会校验排后面的组* @param beanClass* @return*/private List<Class<?>> getDefaultGroupSequence(Class<?> beanClass) {GroupSequence groupSequenceAnnotation = beanClass.getAnnotation( GroupSequence.class );return groupSequenceAnnotation != null ? Arrays.asList( groupSequenceAnnotation.value() ) : null;}/*** 获取类中定义的动态分组的约定程序* @param beanClass* @param <T>* @return*/private <T> DefaultGroupSequenceProvider<? super T> getDefaultGroupSequenceProvider(Class<T> beanClass) {// 判断对应的bean是否添加了GroupSequenceProvider注解GroupSequenceProvider groupSequenceProviderAnnotation = beanClass.getAnnotation( GroupSequenceProvider.class );// 只有添加了GroupSequenceProvider注解才会返回动态分组的约定程序if ( groupSequenceProviderAnnotation != null ) {// 获取GroupSequenceProvider注解中指定的约定程序类,该类实现了DefaultGroupSequenceProvider接口@SuppressWarnings("unchecked")Class<? extends DefaultGroupSequenceProvider<? super T>> providerClass =(Class<? extends DefaultGroupSequenceProvider<? super T>>) groupSequenceProviderAnnotation.value();// 创建一个自定义的DefaultGroupSequenceProvider实例return newGroupSequenceProviderClassInstance( beanClass, providerClass );}return null;}/*** 创建一个DefaultGroupSequenceProvider实例,getValidationGroups()方法的参数必现是beanClass类型* @param beanClass* @param providerClass* @param <T>* @return*/private <T> DefaultGroupSequenceProvider<? super T> newGroupSequenceProviderClassInstance(Class<T> beanClass,Class<? extends DefaultGroupSequenceProvider<? super T>> providerClass) {Method[] providerMethods = run( GetMethods.action( providerClass ) );for ( Method method : providerMethods ) {Class<?>[] paramTypes = method.getParameterTypes();// 判断DefaultGroupSequenceProvider接口的getValidationGroups()方法的参数是否为beanClass类型if ( "getValidationGroups".equals( method.getName() ) && !method.isBridge()&& paramTypes.length == 1 && paramTypes[0].isAssignableFrom( beanClass ) ) {return run(NewInstance.action( providerClass, "the default group sequence provider" ));}}throw LOG.getWrongDefaultGroupSequenceProviderTypeException( beanClass );}}

在retrieveBeanConfiguration()方法中,会调用getDefaultGroupSequence()和getDefaultGroupSequenceProvider()分别解析@GroupSequence和@GroupSequenceProvider注解的分组序列及自定义的DefaultGroupSequenceProvider对象,并保存到BeanConfiguration中。

1.2 通过BeanMetaDataBuilder.add(beanConfiguration)方法中,获取约束元素,获取beanConfiguration中的sequenceSource和defaultGroupSequence保存到builder中。

1.3 执行builder.build(),new一个BeanMetaDataImpl对象,将类中添加的约束信息封装成BeanMetaDataImpl对象。在BeanMetaDataImpl的构造方法中,对分组系列及动态分组系列程序进行解析处理。

/*** defaultGroupSequence:分组序列组。在AnnotaionMetaDataProvider.getDefaultGroupSequence()方法中获取,*						如果没有设置@GroupSequence,则该值为null* defaultGroupSequenceProvider:动态分组约定程序,自定义的DefaultGroupSequenceProvider对象。*						在defaultGroupSequence:分组序列组。在AnnotaionMetaDataProvider.getDefaultGroupSequenceProvider()中获取,如果没有为null* constraintMetaDataSet:类及父类的所有属性、构造器、方法添加的约束元数据*/public BeanMetaDataImpl(Class<T> beanClass,List<Class<?>> defaultGroupSequence,DefaultGroupSequenceProvider<? super T> defaultGroupSequenceProvider,Set<ConstraintMetaData> constraintMetaDataSet,ValidationOrderGenerator validationOrderGenerator) {// 保存基础信息this.validationOrderGenerator = validationOrderGenerator;this.beanClass = beanClass;this.propertyMetaDataMap = newHashMap();Set<PropertyMetaData> propertyMetaDataSet = newHashSet();Set<ExecutableMetaData> executableMetaDataSet = newHashSet();Set<Signature> tmpUnconstrainedExecutables = newHashSet();boolean hasConstraints = false;Set<MetaConstraint<?>> allMetaConstraints = newHashSet();// 对constraintMetaDataSet进行分类,分成属性、类、方法for ( ConstraintMetaData constraintMetaData : constraintMetaDataSet ) {boolean elementHasConstraints = constraintMetaData.isCascading() || constraintMetaData.isConstrained();hasConstraints |= elementHasConstraints;// 属性添加的约束if ( constraintMetaData.getKind() == ElementKind.PROPERTY ) {propertyMetaDataSet.add( (PropertyMetaData) constraintMetaData );}// 类添加的约束else if ( constraintMetaData.getKind() == ElementKind.BEAN ) {allMetaConstraints.addAll( ( (ClassMetaData) constraintMetaData ).getAllConstraints() );}else {// 方法添加的约束ExecutableMetaData executableMetaData = (ExecutableMetaData) constraintMetaData;if ( elementHasConstraints ) {executableMetaDataSet.add( executableMetaData );}else {tmpUnconstrainedExecutables.addAll( executableMetaData.getSignatures() );}}}// 级联Set<Cascadable> cascadedProperties = newHashSet();for ( PropertyMetaData propertyMetaData : propertyMetaDataSet ) {propertyMetaDataMap.put( propertyMetaData.getName(), propertyMetaData );cascadedProperties.addAll( propertyMetaData.getCascadables() );allMetaConstraints.addAll( propertyMetaData.getAllConstraints() );}this.hasConstraints = hasConstraints;this.cascadedProperties = CollectionHelper.toImmutableSet( cascadedProperties );this.allMetaConstraints = CollectionHelper.toImmutableSet( allMetaConstraints );this.classHierarchyWithoutInterfaces = CollectionHelper.toImmutableList( ClassHierarchyHelper.getHierarchy(beanClass,Filters.excludeInterfaces()) );// 转换并将分组序列信息存放在分组序列上下文中DefaultGroupSequenceContext<? super T> defaultGroupContext = getDefaultGroupSequenceData( beanClass, defaultGroupSequence, defaultGroupSequenceProvider, validationOrderGenerator );this.defaultGroupSequenceProvider = defaultGroupContext.defaultGroupSequenceProvider;this.defaultGroupSequence = CollectionHelper.toImmutableList( defaultGroupContext.defaultGroupSequence );this.validationOrder = defaultGroupContext.validationOrder;this.directMetaConstraints = getDirectConstraints();this.executableMetaDataMap = CollectionHelper.toImmutableMap( bySignature( executableMetaDataSet ) );this.unconstrainedExecutables = CollectionHelper.toImmutableSet( tmpUnconstrainedExecutables );// 如果有通过@GroupSequence添加序列组或定义DefaultGroupSequenceProvider,// 则defaultGroupSequenceRedefined为truethis.defaultGroupSequenceRedefined = this.defaultGroupSequence.size() > 1 || hasDefaultGroupSequenceProvider();// 如果有动态分组序列,执行DefaultGroupSequenceProvider的getValidationGroups()方法获取分组,否则返回null// 由于此处传入getValidationGroups()的值为null,所以在自定义的DefaultGroupSequenceProvider// 类的getValidationGroups()方法要对参数进行判空处理this.resolvedDefaultGroupSequence = getDefaultGroupSequence( null );}/*** 转换并将分组序列信息存放在分组序列上下文中*/private static <T> DefaultGroupSequenceContext<T> getDefaultGroupSequenceData(Class<?> beanClass, List<Class<?>> defaultGroupSequence, DefaultGroupSequenceProvider<? super T> defaultGroupSequenceProvider, ValidationOrderGenerator validationOrderGenerator) {if ( defaultGroupSequence != null && defaultGroupSequenceProvider != null ) {throw LOG.getInvalidDefaultGroupSequenceDefinitionException();}DefaultGroupSequenceContext<T> context = new DefaultGroupSequenceContext<>();// 如果有动态分组序列,则分组序列defaultGroupSequence标记为空,不考虑校验顺序if ( defaultGroupSequenceProvider != null ) {context.defaultGroupSequenceProvider = defaultGroupSequenceProvider;context.defaultGroupSequence = Collections.emptyList();context.validationOrder = null;}// 否则的话,如果有分组系列,解析获取分组系列else if ( defaultGroupSequence != null && !defaultGroupSequence.isEmpty() ) {context.defaultGroupSequence = getValidDefaultGroupSequence( beanClass, defaultGroupSequence );context.validationOrder = validationOrderGenerator.getDefaultValidationOrder( beanClass, context.defaultGroupSequence );}else {// 使用默认分组context.defaultGroupSequence = DEFAULT_GROUP_SEQUENCE;context.validationOrder = ValidationOrder.DEFAULT_SEQUENCE;}return context;}/*** 获取分组序列。必须添加beanClass类作为分组中的元素,且会转换为Default分组*/private static List<Class<?>> getValidDefaultGroupSequence(Class<?> beanClass, List<Class<?>> groupSequence) {List<Class<?>> validDefaultGroupSequence = new ArrayList<>();boolean groupSequenceContainsDefault = false;if ( groupSequence != null ) {for ( Class<?> group : groupSequence ) {// 如果分组序列中存在类名的分组,则添加Default分组,且groupSequenceContainsDefault为trueif ( group.getName().equals( beanClass.getName() ) ) {validDefaultGroupSequence.add( Default.class );groupSequenceContainsDefault = true;}// 如果在分组系列中添加了Default分组,则抛异常else if ( group.getName().equals( Default.class.getName() ) ) {throw LOG.getNoDefaultGroupInGroupSequenceException();}else {// 添加分组validDefaultGroupSequence.add( group );}}}// 如果groupSequenceContainsDefault为false,即在分组序列中没有添加对应类名的分组,则抛异常if ( !groupSequenceContainsDefault ) {throw LOG.getBeanClassMustBePartOfRedefinedDefaultGroupSequenceException( beanClass );}if ( LOG.isTraceEnabled() ) {LOG.tracef("Members of the default group sequence for bean %s are: %s.",beanClass.getName(),validDefaultGroupSequence);}return validDefaultGroupSequence;}/*** 如果有动态分组序列,执行DefaultGroupSequenceProvider的getValidationGroups()方法获取分组,否则返回分组系列*/@Overridepublic List<Class<?>> getDefaultGroupSequence(T beanState) {if ( hasDefaultGroupSequenceProvider() ) {List<Class<?>> providerDefaultGroupSequence = defaultGroupSequenceProvider.getValidationGroups( beanState );return getValidDefaultGroupSequence( beanClass, providerDefaultGroupSequence );}return defaultGroupSequence;}/*** 获取当前beanClass的分组序列。如果定义了DefaultGroupSequenceProvider,则执行getValidationGroups(),获得动态分组序列Sequence集合*/@Overridepublic Iterator<Sequence> getDefaultValidationSequence(T beanState) {// 如果有动态分组序列if ( hasDefaultGroupSequenceProvider() ) {// 执行DefaultGroupSequenceProvider的getValidationGroups(),获取动态分组序列List<Class<?>> providerDefaultGroupSequence = defaultGroupSequenceProvider.getValidationGroups( beanState );return validationOrderGenerator.getDefaultValidationOrder(beanClass,// 获取分组序列。必须添加类名的分组,且会转换为Default分组。此处说明在自定义// 的DefaultGroupSequenceProvider.getValidationGroups()方法中返回的分组序列也必须包含beanClass类作为分组getValidDefaultGroupSequence( beanClass, providerDefaultGroupSequence )).getSequenceIterator();}else {return validationOrder.getSequenceIterator();}}
}

注:通过构造器的最后一行代码调用了getDefaultGroupSequence(null),传入的值为null。如果有动态分组程序,该方法会执行DefaultGroupSequenceProvider.getValidationGroups(),且传入null,所以要在该方法中进行判空处理。另外,结合getValidDefaultGroupSequence()方法的代码,在使用@GroupSequence时,需要添加beanClass的分组作为默认分组,该方法会自动转换为Default分组。

二、分组序列校验

validation校验的时候,会调用ValidatorImol的validate()或validateXXX()方法,不明白的可以看前面的博文。ValidatorImol.validate()代码如下:

    @Overridepublic final <T> Set<ConstraintViolation<T>> validate(T object, Class<?>... groups) {Contracts.assertNotNull( object, MESSAGES.validatedObjectMustNotBeNull() );sanityCheckGroups( groups );@SuppressWarnings("unchecked")Class<T> rootBeanClass = (Class<T>) object.getClass();// 从BeanMetaDataManager中获取类对应的BeanMetaData对象,为BeanMetaDataImpl实例BeanMetaData<T> rootBeanMetaData = beanMetaDataManager.getBeanMetaData( rootBeanClass );if ( !rootBeanMetaData.hasConstraints() ) {return Collections.emptySet();}BaseBeanValidationContext<T> validationContext = getValidationContextBuilder().forValidate( rootBeanClass, rootBeanMetaData, object );// 确定组验证顺序ValidationOrder validationOrder = determineGroupValidationOrder( groups );BeanValueContext<?, Object> valueContext = ValueContexts.getLocalExecutionContextForBean(validatorScopedContext.getParameterNameProvider(),object,validationContext.getRootBeanMetaData(),PathImpl.createRootPath());// 验证return validateInContext( validationContext, valueContext, validationOrder );}private ValidationOrder determineGroupValidationOrder(Class<?>[] groups) {Collection<Class<?>> resultGroups;// 没有添加分组的话,默认为Default分组if ( groups.length == 0 ) {resultGroups = DEFAULT_GROUPS;}else {resultGroups = Arrays.asList( groups );}// 获取验证组顺序return validationOrderGenerator.getValidationOrder( resultGroups );}

2.1 在该方法中,先从BeanMetaDataManager中获取类对应的BeanMetaData对象,及上面1.3中讲解的BeanMetaDataImpl对象。

2.2 然后执行determineGroupValidationOrder(Class<?>[] groups),其中的groups为validate()方法中传入的,为@Validated注解中添加的,默认为Default分组。该方法调用validationOrderGenerator.getValidationOrder( resultGroups )获取分组顺序。

public class ValidationOrderGenerator {private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() );private final ConcurrentMap<Class<?>, Sequence> resolvedSequences = new ConcurrentHashMap<Class<?>, Sequence>();/*** 为指定的验证组生成组和序列的顺序,返回验证的执行顺序对象ValidationOrder*/public ValidationOrder getValidationOrder(Collection<Class<?>> groups) {// 必须有分组if ( groups == null || groups.size() == 0 ) {throw LOG.getAtLeastOneGroupHasToBeSpecifiedException();}// 如果只有Default.class分组,返回默认分组顺序if ( groups.size() == 1 && groups.contains( Default.class ) ) {return ValidationOrder.DEFAULT_GROUP;}// 分组必须的接口for ( Class<?> clazz : groups ) {if ( !clazz.isInterface() ) {throw LOG.getGroupHasToBeAnInterfaceException( clazz );}}DefaultValidationOrder validationOrder = new DefaultValidationOrder();// 遍历分组for ( Class<?> clazz : groups ) {if ( Default.class.equals( clazz ) ) { // HV-621validationOrder.insertGroup( Group.DEFAULT_GROUP );}// 如果分组中添加了@GroupSequence注解else if ( isGroupSequence( clazz ) ) {insertSequence( clazz, clazz.getAnnotation( GroupSequence.class ).value(), true, validationOrder );}else {Group group = new Group( clazz );// 将分组添加到validationOrder,在validationOrder对象中,使用List<Group>存放validationOrder.insertGroup( group );// 将继承的组递归添加到组链List<Group>中insertInheritedGroups( clazz, validationOrder );}}return validationOrder;}/*** 获取验证顺序。将defaultGroupSequence中的分组和clazz组成Sequence,添加到ValidationOrder*/public ValidationOrder getDefaultValidationOrder(Class<?> clazz, List<Class<?>> defaultGroupSequence) {// 定义一个DefaultValidationOrder对象DefaultValidationOrder validationOrder = new DefaultValidationOrder();// 以clazz为单位【当前分组类】,记录分组序列中的分组,封装成Sequence对象,保存到validationOrder中insertSequence( clazz, defaultGroupSequence.toArray( new Class<?>[defaultGroupSequence.size()] ), false, validationOrder );return validationOrder;}private boolean isGroupSequence(Class<?> clazz) {return clazz.getAnnotation( GroupSequence.class ) != null;}/*** 将继承的组递归添加到组链中*/private void insertInheritedGroups(Class<?> clazz, DefaultValidationOrder chain) {for ( Class<?> inheritedGroup : clazz.getInterfaces() ) {Group group = new Group( inheritedGroup );chain.insertGroup( group );insertInheritedGroups( inheritedGroup, chain );}}/*** 以sequenceClass为单位,记录分组序列中的分组,封装成Sequence对象,保存到validationOrder中*/private void insertSequence(Class<?> sequenceClass, Class<?>[] sequenceElements, boolean cache, DefaultValidationOrder validationOrder) {// 如果是@GroupSequence注解中的分组序列,cache为true,会进行缓存Sequence sequence = cache ? resolvedSequences.get( sequenceClass ) : null;if ( sequence == null ) {// 解析sequenceClass中的所有分组系列,如果分组系列中的分组还有@GroupSequence,递归调用,全部添加到ArrayList中sequence = resolveSequence( sequenceClass, sequenceElements, new ArrayList<Class<?>>() );// 只有在确定序列是否可扩展后,才能扩展继承的组sequence.expandInheritedGroups();// 缓存if ( cache ) {final Sequence cachedResolvedSequence = resolvedSequences.putIfAbsent( sequenceClass, sequence );if ( cachedResolvedSequence != null ) {sequence = cachedResolvedSequence;}}}// 添加到validationOrder的Map属性中,key为分组类名称,value为分组类中添加的@GroupSequence分组validationOrder.insertSequence( sequence );}/*** 遍历sequenceClass分组中的@GroupSequence分组sequenceElements到List中,封装成Sequence对象。如果分组中还有@GroupSequence,则递归调用*/private Sequence resolveSequence(Class<?> sequenceClass, Class<?>[] sequenceElements, List<Class<?>> processedSequences) {// 如果sequenceClass分组已经处理了,则抛异常if ( processedSequences.contains( sequenceClass ) ) {throw LOG.getCyclicDependencyInGroupsDefinitionException();}else {// 将sequenceClass分组添加到已处理集合中processedSequences.add( sequenceClass );}List<Group> resolvedSequenceGroups = new ArrayList<Group>();// 遍历分组序列中的分组for ( Class<?> clazz : sequenceElements ) {// 继续判断对应的分组添加了@GroupSequence,则递归遍历@GroupSequence中的分组序列if ( isGroupSequence( clazz ) ) {Sequence tmpSequence = resolveSequence( clazz, clazz.getAnnotation( GroupSequence.class ).value(), processedSequences );// 将分组中添加的@GroupSequence序列分组信息添加到resolvedSequenceGroups中addGroups( resolvedSequenceGroups, tmpSequence.getComposingGroups() );}else {List<Group> list = new ArrayList<Group>();list.add( new Group( clazz ) );// 将分组添加到resolvedSequenceGroups中addGroups( resolvedSequenceGroups, list );}}return new Sequence( sequenceClass, resolvedSequenceGroups );}/*** 将groups中的分组信息添加到resolvedGroupSequence中*/private void addGroups(List<Group> resolvedGroupSequence, List<Group> groups) {for ( Group tmpGroup : groups ) {if ( resolvedGroupSequence.contains( tmpGroup ) && resolvedGroupSequence.indexOf( tmpGroup ) < resolvedGroupSequence.size() - 1 ) {throw LOG.getUnableToExpandGroupSequenceException();}resolvedGroupSequence.add( tmpGroup );}}}

如果分组添加了@GroupSequence,则会遍历分组序列,如果分组序列的分组还添加了@GroupSequence,会循环递归遍历。解析所有的分组,封装成Sequence对象。保存到ValidationOrder对象中。

注:在自定义的DefaultGroupSequenceProvider.getValidationGroups()方法中,返回的分组数组必须包含beanClass作为分组

2.3 执行validateInContext()方法

public class ValidatorImpl implements Validator, ExecutableValidator {private <T, U> Set<ConstraintViolation<T>> validateInContext(BaseBeanValidationContext<T> validationContext, BeanValueContext<U, Object> valueContext,ValidationOrder validationOrder) {if ( valueContext.getCurrentBean() == null ) {return Collections.emptySet();}BeanMetaData<U> beanMetaData = valueContext.getCurrentBeanMetaData();// 判断当前bean是否添加了@DefaultGroupSequenceif ( beanMetaData.isDefaultGroupSequenceRedefined() ) {// 先执行beanMetaData.getDefaultGroupSequence(valueContext.getCurrentBean()),此时传入了实体对象。// 如果有动态分组系列程序DefaultGroupSequenceProvider,则会再次执行// DefaultGroupSequenceProvider.getValidationGroups()方法,动态获取分组。// validationOrder.assertDefaultGroupSequenceIsExpandable()默认为空方法validationOrder.assertDefaultGroupSequenceIsExpandable( beanMetaData.getDefaultGroupSequence( valueContext.getCurrentBean() ) );}// 处理第一个单组。对于这些,可以通过在遍历对象之前首先在当前bean上运行所有验证来优化对象遍历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();}}// 处理分组序列。对于序列,必须遍历对象图,因为当发生错误时,必须停止处理。// 获取分组序列集合,并遍历SequenceIterator<Sequence> sequenceIterator = validationOrder.getSequenceIterator();while ( sequenceIterator.hasNext() ) {Sequence sequence = sequenceIterator.next();// 遍历sequence中的扩展组,一组系列分组会存放在一个扩展组中。此处感觉有些多余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;}}}// 校验上下文的错误消息,所有的校验注解之间的上下文ConstraintValidatorContext是完全独立的,无法互相访问通信return validationContext.getFailingConstraints();}private void validateConstraintsForCurrentGroup(BaseBeanValidationContext<?> validationContext, BeanValueContext<?, Object> valueContext) {// 如果不是Default默认组,调用validateConstraintsForNonDefaultGroup(),直接进行验证if ( !valueContext.validatingDefault() ) {validateConstraintsForNonDefaultGroup( validationContext, valueContext );}// 如果是验证默认组序列,需要考虑层次结构中的类可以重新定义默认组序列else {validateConstraintsForDefaultGroup( validationContext, valueContext );}}private <U> void validateConstraintsForDefaultGroup(BaseBeanValidationContext<?> validationContext, BeanValueContext<U, Object> valueContext) {final BeanMetaData<U> beanMetaData = valueContext.getCurrentBeanMetaData();final Map<Class<?>, Class<?>> validatedInterfaces = new HashMap<>();// 评估层次结构中每个类的bean约束,这对于检测潜在的默认组重新定义是必要的for ( Class<? super U> clazz : beanMetaData.getClassHierarchy() ) {// 获取父类的BeanMetaDataBeanMetaData<? super U> hostingBeanMetaData = beanMetaDataManager.getBeanMetaData( clazz );// 获取是否定义了默认组序列。如果通过组序列重新定义或组序列提供程序重新定义了默认组序列,返回trueboolean defaultGroupSequenceIsRedefined = hostingBeanMetaData.isDefaultGroupSequenceRedefined();// 如果当前类重新定义了默认的组序列,则必须将该序列应用于所有类层次结构if ( defaultGroupSequenceIsRedefined ) {// 获取当前beanClass的分组序列。如果定义了DefaultGroupSequenceProvider,则执行getValidationGroups(),获得动态分组序列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 ) {// 执行当前组的校验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 {Set<MetaConstraint<?>> metaConstraints = hostingBeanMetaData.getDirectMetaConstraints();validateConstraintsForSingleDefaultGroupElement( validationContext, valueContext, validatedInterfaces, clazz, metaConstraints,Group.DEFAULT_GROUP );validationContext.markCurrentBeanAsProcessed( valueContext );}// all constraints in the hierarchy has been validated, stop validation.if ( defaultGroupSequenceIsRedefined ) {break;}}}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中valueContext.setCurrentGroup( defaultSequenceMember.getDefiningClass() );for ( MetaConstraint<?> metaConstraint : metaConstraints ) {// 在层次结构中多次实现的接口只需验证一次。一个接口可以定义多个约束,必须检查正在验证的类。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 );}// 执行验证boolean tmp = validateMetaConstraint( validationContext, valueContext, valueContext.getCurrentBean(), metaConstraint );if ( shouldFailFast( validationContext ) ) {return false;}validationSuccessful = validationSuccessful && tmp;}return validationSuccessful;}/*** 非默认组,调用validateMetaConstraints()进行校验*/private void validateConstraintsForNonDefaultGroup(BaseBeanValidationContext<?> validationContext, BeanValueContext<?, Object> valueContext) {// 调用validateMetaConstraints(),逐个遍历约束元数据,执行约束中的isValid()进行校验validateMetaConstraints( validationContext, valueContext, valueContext.getCurrentBean(), valueContext.getCurrentBeanMetaData().getMetaConstraints() );validationContext.markCurrentBeanAsProcessed( valueContext );}
}

在该方法中,如果有序列分组或动态序列分组,会先执行动态序列分组的DefaultGroupSequenceProvider.getValidationGroups()获取分组序列。然后遍历分组序列,执行对应分组的校验。具体的校验方法validateMetaConstraint(),不明白的可以看。

【源码】Spring validation参数校验原理解析之Controller控制器参数校验中@RequestBody参数校验实现原理_simpleconstrainttree-CSDN博客

小结

本篇的源码量比较大,细节也比较多。建议如果本篇不太理解的,可以看看该系列的上一篇或者下一篇源码讲解文章。

关于本篇内容你有什么自己的想法或独到见解,欢迎在评论区一起交流探讨,一起学习。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/1951.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

苍穹外卖day8(2)用户下单、微信支付

文章目录 前言一、用户下单1. 业务流程2. 接口设计3. 数据库设计3.1 订单表orders3.2 订单明细表 order_detail 4. 代码实现 二、订单支付 前言 用户下单 因为订单信息中包含了其他业务中的数据&#xff0c;在逻辑处理中涉及了多个其他业务&#xff0c;比如要判断地址簿、购物…

描述一下PHP与HTML和CSS的关系

描述一下PHP与HTML和CSS的关系 PHP、HTML和CSS在Web开发中各自扮演着不同的角色&#xff0c;它们之间的关系密切而互补&#xff0c;共同构建了一个完整且富有动态性的Web应用程序。下面我将详细阐述PHP与HTML和CSS之间的关系。 首先&#xff0c;HTML&#xff08;HyperText Ma…

面试复习基础题目-c#相关

面试复习基础题目 c#相关问题 delegate和event的区别是什么&#xff1f; Delegate用来声明委托类型&#xff0c;event用来声明委托对象&#xff1b; 事件是委托的一种应用&#xff0c;事件是带有event关键词的委托对象&#xff0c;对委托对象进行了封装&#xff0c;本质就是委…

聚类与分类的区别

聚类和分类是机器学习中的两个基本概念&#xff0c;两者的主要区别在于用于分类的数据已经预先标记好类别&#xff0c;而用于聚类的数据则没有预先标记的类别。以下是详细介绍&#xff1a; 目的不同。聚类的目的是发现数据中的自然分组&#xff0c;将相似或相关的对象组织在一…

基于SSM+Vue的护工预约服务小程序和后台管理系统

1、系统演示视频&#xff08;演示视频&#xff09; 2、需要请联系

虚拟化+Docker基本管理

一、虚拟化简介 1、云端 华为云、谷歌云、腾讯云、阿里云、亚马逊、百度云、移动云、天翼云、西部数码云等 1.国内云 华为云、阿里云、腾讯云、天翼云(私有云) 2.国外云 谷歌云、亚马逊 2、云计算的服务模式是分层的 IaaS&#xff1a;Infrastructure&#xff08;基础设…

计算机网络【CN】Ch3 数据链路层

目录 数据链路层的功能 【※】VLAN 三种划分VLAN的方法&#xff1a; 【※】MAC帧格式 【※】三种可靠传输机制 ​编辑 【※】介质访问控制 信道划分介质访问控制 随机介质访问控制 CSMA CSMA/CD【有线】 CSMA/CA【无线】 信道利用率技巧 循环冗余校验CRC 以太网[802.3] 以太网…

项目实战 | 责任链模式 (下)

案例二&#xff1a;工作流&#xff0c;费用报销审核流程 同事小贾最近刚出差回来&#xff0c;她迫不及待的就提交了费用报销的流程。根据金额不同&#xff0c;分为以下几种审核流程。报销金额低于1000元&#xff0c;三级部门管理者审批即可&#xff0c;1000到5000元除了三级部…

【k8s】(五)kubernetes1.29.4离线部署之-初始化第一个控制平面

备注&#xff1a; 完整版请参阅 【k8s】Kubernetes 1.29.4离线安装部署&#xff08;总&#xff09; 执行命令初始化第一个控制平面节点 在上节的安装过程中&#xff0c;实际以及包含了初始化第一个控制平面的脚本&#xff0c;由于其重要性&#xff0c;这里单独提出来详细说明。…

RK平台HDMI和喇叭同时出声

多屏时&#xff0c;未接入hdmi默认speaker音频输出&#xff0c;检测到接入hdmi时切换到hdmi音频输出。此时有个问题&#xff0c;如果接入hdmi的屏没有speaker会就导致没有声音输出了&#xff0c;所以需要默认hdmi和speaker同时出声。 /frameworks/base/services/core/java/com…

智己汽车数据驱动中心PMO高级经理张晶女士受邀为第十三届中国PMO大会演讲嘉宾

全国PMO专业人士年度盛会 智己汽车科技有限公司数据驱动中心PMO高级经理张晶女士受邀为PMO评论主办的2024第十三届中国PMO大会演讲嘉宾&#xff0c;演讲议题为“规模化敏捷落地实践”。大会将于5月25-26日在北京举办&#xff0c;敬请关注&#xff01; 议题简要&#xff1a; 2…

【Java学习笔记】9.5 Java中的Lambda表达式

Lambda表达式是从Java8版本开始增加的语法。Lambda表达式有利于实现函数式编程&#xff0c;简化开发。 9.5.1 Lambda表达式入门 Lambda表达式由三部分组成&#xff1a;参数列表、箭头(->),及一个表达式或语句块。其完整的语法格式如下&#xff1a; (Type 1 param1 , Type…

hcip实验 — 路由策略实验

目录 实验拓扑 实验要求 实验思路 实验步骤 1.配置接口及环回ip 2.配置ospf协议及rip协议 3.在R2上进行路由引入 4.在R2上进行路由过滤 5.在R4上进行路由过滤&#xff08;地址前缀列表&#xff09; 6.在R2 RIP进程上配置静默接口使RIP报文无法进入OSPF区域 实验拓扑 …

Linux默认shell简介、查看和更改

在Linux环境中&#xff0c;用户的“默认shell”是指他们登录系统后自动启动的命令行解释器或交互式shell。这个shell用于处理用户在命令行界面输入的命令和操作。每个用户都拥有一个特定的默认shell。 Linux系统中常见的默认shell包括以下几种&#xff1a; Bash&#xff08;B…

HCF-Net:用于红外小目标检测的分层上下文融合网络

摘要 红外小目标检测是一项重要的计算机视觉任务&#xff0c;涉及在红外图像中识别和定位微小物体&#xff0c;这些物体通常仅包含几个像素。然而&#xff0c;由于物体尺寸极小以及红外图像中通常复杂的背景&#xff0c;这项任务面临困难。在本文中&#xff0c;我们提出了一种…

[leetcode] 264. 丑数 II

给你一个整数 n &#xff0c;请你找出并返回第 n 个 丑数 。 丑数 就是质因子只包含 2、3 和 5 的正整数。 示例 1&#xff1a; 输入&#xff1a;n 10 输出&#xff1a;12 解释&#xff1a;[1, 2, 3, 4, 5, 6, 8, 9, 10, 12] 是由前 10 个丑数组成的序列。示例 2&#xff1…

贪吃蛇代码实现

一.基本信息 实现目标&#xff1a;使用C语言在Windows环境的控制台中实现贪吃蛇游戏 游戏运行&#xff1a; 地图绘制基本玩法提示信息游戏的开始与结束 基本玩法&#xff1a; 通过上下左右键控制蛇的移动蛇可以加速减速吃掉食物可以得分并增加蛇的长度可以自动暂停 游戏结…

代码随想录算法训练营第三十四天|1005.K次取反后最大化的数组和、134. 加油站、135. 分发糖果

1005.K次取反后最大化的数组和 思路&#xff1a; 贪心的思路&#xff0c;局部最优&#xff1a;让绝对值大的负数变为正数&#xff0c;当前数值达到最大&#xff0c;整体最优&#xff1a;整个数组和达到最大。 局部最优可以推出全局最优。 那么如果将负数都转变为正数了&…

SAP DMS后台配置(4)

当DMS和CS文档服务器关联的时候,除了需要BASIS进行相关的配置以外,我们DMS也要进行相关的系统配置 路径:SPRO—跨应用组件—文档管理—常规数据—存储系统设置—维护还原系统 路径:SPRO—跨应用组件—文档管理—常规数据—存储系统设置—维护还原类别 当DMS和CS文档服…

HarmonyOS开发实例:【图片编辑应用】

介绍 本篇Codelab通过动态设置元素样式的方式&#xff0c;实现几种常见的图片操作&#xff0c;包括裁剪、旋转、缩放和镜像。效果如图所示&#xff1a; 相关概念 [image组件]&#xff1a;图片组件&#xff0c;用来渲染展示图片。[div组件]&#xff1a;基础容器组件&#xff0…