javax.validation 随笔1 乱序的根因

0. 摆久了也是蛮累的

也许去年,还是什么时候,当时注意到校验时的无序问题时,只觉得是内部有个用于校验的线程池(想复杂了)。一直没有“档期”去细看,其实蛮简单的


比较简单的框架,直接就从调用堆栈开始吧

// ConstraintValidator
isValid:22, NotNullValidator (org.hibernate.validator.internal.constraintvalidators.bv)

validateSingleConstraint:180, ConstraintTree (org.hibernate.validator.internal.engine.constraintvalidation)
validateConstraints:62, SimpleConstraintTree (org.hibernate.validator.internal.engine.constraintvalidation)
validateConstraints:75, ConstraintTree (org.hibernate.validator.internal.engine.constraintvalidation)
doValidateConstraint:130, MetaConstraint (org.hibernate.validator.internal.metadata.core)
validateConstraint:123, MetaConstraint (org.hibernate.validator.internal.metadata.core)
validateMetaConstraint:555, ValidatorImpl (org.hibernate.validator.internal.engine)

// step into ...
// 这里 所有的ConstraintValidators 循环校验
// 说明并非并发校验所致
// 我们可以看下这个集合是怎么来的
validateConstraintsForSingleDefaultGroupElement:518, ValidatorImpl (org.hibernate.validator.internal.engine)
validateConstraintsForDefaultGroup:488, ValidatorImpl (org.hibernate.validator.internal.engine)
validateConstraintsForCurrentGroup:450, ValidatorImpl (org.hibernate.validator.internal.engine)
validateInContext:400, ValidatorImpl (org.hibernate.validator.internal.engine)
validate:172, ValidatorImpl (org.hibernate.validator.internal.engine)
validate:358, SpringValidatorAdapter (org.springframework.validation.beanvalidation)

// test-class
t0:29, ValidationTest (cn.angel.boot.demo.validation)

附上test-class

package cn.angel.boot.demo.validation;

import lombok.AllArgsConstructor;
import lombok.Data;
import org.springframework.stereotype.Component;

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;

/**
 * @author weng
 * @since 2023/6/12
 */
@AllArgsConstructor
@Data
public class ValidationBean {
    @NotBlank(message = "name不能为空串")
    String name;

    @NotNull(message = "id不能为空")
    Long id;
}

1. 首先,得知道这里的循环只是普通的循环

// org.hibernate.validator.internal.engine.ValidatorImpl#validateConstraintsForSingleDefaultGroupElement
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() );

	// 普通的循环而已
	// 说明这个集合已经乱了(这是个set类型)
	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 );
		}

		boolean tmp = validateMetaConstraint( validationContext, valueContext, valueContext.getCurrentBean(), metaConstraint );
		if ( shouldFailFast( validationContext ) ) {
			return false;
		}

		validationSuccessful = validationSuccessful && tmp;
	}
	return validationSuccessful;
}

2. 然后,得知道这个Set意味着hash存储的方式

// org.hibernate.validator.internal.engine.ValidatorImpl#validateConstraintsForDefaultGroup
private <U> void validateConstraintsForDefaultGroup(BaseBeanValidationContext<?> validationContext, BeanValueContext<U, Object> valueContext) {
	final BeanMetaData<U> beanMetaData = valueContext.getCurrentBeanMetaData();
	final Map<Class<?>, Class<?>> validatedInterfaces = new HashMap<>();

	// evaluating the constraints of a bean per class in hierarchy, this is necessary to detect potential default group re-definitions
	for ( Class<? super U> clazz : beanMetaData.getClassHierarchy() ) {
		BeanMetaData<? super U> hostingBeanMetaData = beanMetaDataManager.getBeanMetaData( clazz );
		boolean defaultGroupSequenceIsRedefined = hostingBeanMetaData.isDefaultGroupSequenceRedefined();

		// if the current class redefined the default group sequence, this sequence has to be applied to all the class hierarchy.
		if ( defaultGroupSequenceIsRedefined ) {
			Iterator<Sequence> defaultGroupSequence = hostingBeanMetaData.getDefaultValidationSequence( valueContext.getCurrentBean() );
			Set<MetaConstraint<?>> metaConstraints = hostingBeanMetaData.getMetaConstraints();

			while ( defaultGroupSequence.hasNext() ) {
				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 redefined
		else {
			// 有方向了
			// org.hibernate.validator.internal.metadata.aggregated.BeanMetaDataImpl#getDirectMetaConstraints
			// return directMetaConstraints;
			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;
		}
	}
}

3. 追溯到变量赋值的地方

被校验bean的校验元信息解析的地方

// 看来只有这里可以赋值这个集合了
// org.hibernate.validator.internal.metadata.aggregated.BeanMetaDataImpl#BeanMetaDataImpl
/**
 * Creates a new {@link BeanMetaDataImpl}
 *
 * @param beanClass The Java type represented by this meta data object.
 * @param defaultGroupSequence The default group sequence.
 * @param defaultGroupSequenceProvider The default group sequence provider if set.
 * @param constraintMetaDataSet All constraint meta data relating to the represented type.
 */
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;
	// 最后又回到了这列
	// 所以说乱序的根因不是并发所致,而是hash分布
	Set<MetaConstraint<?>> allMetaConstraints = newHashSet();

	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;

	// step into ...
	// 这里
	this.directMetaConstraints = getDirectConstraints();

	this.executableMetaDataMap = CollectionHelper.toImmutableMap( bySignature( executableMetaDataSet ) );
	this.unconstrainedExecutables = CollectionHelper.toImmutableSet( tmpUnconstrainedExecutables );

	// We initialize those elements eagerly so that any eventual error is thrown when bootstrapping the bean metadata
	this.defaultGroupSequenceRedefined = this.defaultGroupSequence.size() > 1 || hasDefaultGroupSequenceProvider();
	this.resolvedDefaultGroupSequence = getDefaultGroupSequence( null );
}

private Set<MetaConstraint<?>> getDirectConstraints() {
	Set<MetaConstraint<?>> constraints = newHashSet();

	Set<Class<?>> classAndInterfaces = newHashSet();
	classAndInterfaces.add( beanClass );
	classAndInterfaces.addAll( ClassHierarchyHelper.getDirectlyImplementedInterfaces( beanClass ) );

	for ( Class<?> clazz : classAndInterfaces ) {
		// 搜索一下哪里赋值了这个 allMetaConstraints
		for ( MetaConstraint<?> metaConstraint : allMetaConstraints ) {
			if ( metaConstraint.getLocation().getDeclaringClass().equals( clazz ) ) {
				constraints.add( metaConstraint );
			}
		}
	}

	return CollectionHelper.toImmutableSet( constraints );
}

你可能感兴趣的:(Java,java,jvm,数据库)