当前位置:网站首页>@The function of valid (cascade verification) and the explanation of common constraint annotations
@The function of valid (cascade verification) and the explanation of common constraint annotations
2022-07-28 02:53:00 【Big flicker love flicker】
@Valid The role of ( Cascade check ) And the explanation of common constraint annotation
Group check
@Getter
@Setter
@ToString
public class Person {
@NotNull(message = " Name cannot be empty ", groups = Simple.class)
public String name;
/** * Built in grouping :default */
@Max(value = 10, groups = Simple.class)
@Positive(groups = Default.class)
public Integer age;
@NotNull(groups = Complex.class)
@NotEmpty(groups = Complex.class)
private List<@Email String> emails;
@Future(groups = Complex.class)
private Date start;
// Define two groups Simple Group and Complex Group
public interface Simple {
}
public interface Complex {
}
}
public class ValidationTest {
@Test
public void testValidation(){
Person person = new Person();
//person.setName("fsx");
person.setAge(18);
// email check : Although it is List All of them can be verified
person.setEmails(Arrays.asList("[email protected]", "[email protected]", "aaa.com"));
//person.setStart(new Date()); //start Need is a future time : Sun Jul 21 10:45:03 CST 2019
//person.setStart(new Date(System.currentTimeMillis() + 10000)); // Check by
HibernateValidatorConfiguration configure = Validation.byProvider(HibernateValidator.class).configure();
ValidatorFactory validatorFactory = configure.failFast(false).buildValidatorFactory();
// according to validatorFactory To get a Validator
Validator validator = validatorFactory.getValidator();
// Group check ( Can be treated differently Default Group 、Simple Group 、Complex Group )
Set<ConstraintViolation<Person>> result = validator.validate(person, Person.Simple.class);
//Set<ConstraintViolation<Person>> result = validator.validate(person, Person.Complex.class);
// Traverse the results and output
result.stream().map(v -> v.getPropertyPath() + " " + v.getMessage() + ": " + v.getInvalidValue())
.forEach(System.out::println);
}
}
Run print :
age Maximum not exceeding 10: 18
name {
message} -> The name cannot be null -> The name cannot be null: null
You can see the effect directly , The verification here is only performed Person.Simple.class This Group Constraints on groups ~
Groups are constrained by Spring MVC There are still relatively many usage scenarios in , But here's the thing :javax.validation.Valid The specified group is not provided , however org.springframework.validation.annotation.Validated Extensions provide the ability to specify groups directly at the annotation level
@Valid annotation
We know JSR Provides a @Valid Notes for use , Before this article , Most of my friends are in Controller And combine @RequestBody Use it together , But after this article , You will definitely have a new understanding of it .
This annotation is used to verify the properties of the cascade 、 Method parameter or method return type .
When validating properties 、 Method parameter or method return type , The constraints defined on the object and its properties... Will be validated , in addition : This behavior is applied recursively .
In order to understand @Valid, You have to know when to deal with it :
MetaDataProvider
Metadata provider : Constraint related metadata ( Such as restraint 、 Default group sequence, etc ) Of Provider. Its functions and characteristics are as follows :
- Based on different metadata : Such as xml、 annotation .( There's also a programming map ) There are three types . The corresponding enumeration class is :
public enum ConfigurationSource {
ANNOTATION( 0 ),
XML( 1 ),
API( 2 ); //programmatic API
}
- MetaDataProvider Only the metadata configured directly for a class is returned
- It doesn't deal with superclasses 、 Interface merged metadata ( In short, you @Valid It's invalid to put it at the interface )
public interface MetaDataProvider {
// take Annotation processing options Return it here Provider To configure . Its only implementation class is :AnnotationProcessingOptionsImpl
// It can be configured like :areMemberConstraintsIgnoredFor areReturnValueConstraintsIgnoredFor
// In other words, it can be configured : To keep from being checked ~~~~~~( It's for the green light )
AnnotationProcessingOptions getAnnotationProcessingOptions();
// The return function is here Bean above `BeanConfiguration` If not, go back null 了
// BeanConfiguration hold ConfigurationSource References to ~
<T> BeanConfiguration<? super T> getBeanConfiguration(Class<T> beanClass);
}
// The expression comes from a ConfigurationSource One of the Java Complete constraint related configuration of type . Containing fields 、 Method 、 Metadata at the class level
// There is also metadata on the default group sequence ( Use less )
public class BeanConfiguration<T> {
// Enumeration of three sources
private final ConfigurationSource source;
private final Class<T> beanClass;
// ConstrainedElement Represents the element to be verified , It can be known that it will have the following four subclasses :
// ConstrainedField/ConstrainedType/ConstrainedParameter/ConstrainedExecutable
// Be careful :ConstrainedExecutable What is held is java.lang.reflect.Executable object
// Its two subclasses are java.lang.reflect.Method and Constructor
private final Set<ConstrainedElement> constrainedElements;
private final List<Class<?>> defaultGroupSequence;
private final DefaultGroupSequenceProvider<? super T> defaultGroupSequenceProvider;
... // It doesn't deal with any logic on its own , Parameters are passed in through the constructor
}
Its inheritance tree :
The three implementation classes correspond to the three metadata types mentioned above . This article obviously only needs to focus on the comments related :AnnotationMetaDataProvider
AnnotationMetaDataProvider
This metadata comes from annotation , And then it's Hibernate Validation Default configuration source. It's going to deal with labels that have @Valid The elements of ~
public class AnnotationMetaDataProvider implements MetaDataProvider {
private final ConstraintHelper constraintHelper;
private final TypeResolutionHelper typeResolutionHelper;
private final AnnotationProcessingOptions annotationProcessingOptions;
private final ValueExtractorManager valueExtractorManager;
// This is a very important attribute , It will record the present Bean All to be verified Bean Information ~~~
private final BeanConfiguration<Object> objectBeanConfiguration;
// Unique constructor
public AnnotationMetaDataProvider(ConstraintHelper constraintHelper,
TypeResolutionHelper typeResolutionHelper,
ValueExtractorManager valueExtractorManager,
AnnotationProcessingOptions annotationProcessingOptions) {
this.constraintHelper = constraintHelper;
this.typeResolutionHelper = typeResolutionHelper;
this.valueExtractorManager = valueExtractorManager;
this.annotationProcessingOptions = annotationProcessingOptions;
// By default , It goes to put Object All the methods involved retrieve: Retrieve it and put I don't quite understand the matter ~~~
// I found out later : All for efficiency
this.objectBeanConfiguration = retrieveBeanConfiguration( Object.class );
}
// Implementation interface method
@Override
public AnnotationProcessingOptions getAnnotationProcessingOptions() {
return new AnnotationProcessingOptionsImpl();
}
// If your Bean yes Object I'm going straight back ~~~( Most of the time All are Object)
@Override
@SuppressWarnings("unchecked")
public <T> BeanConfiguration<T> getBeanConfiguration(Class<T> beanClass) {
if ( Object.class.equals( beanClass ) ) {
return (BeanConfiguration<T>) objectBeanConfiguration;
}
return retrieveBeanConfiguration( beanClass );
}
}
As you can see above , The core analytic logic is retrieveBeanConfiguration() In this private way . Summarize the two original entries that call this method ( A constructor , An interface method ):
- ValidatorFactory.getValidator() When getting the calibrator , Initializes itself new One BeanMetaDataManager:

- call Validator.validate() Method time ,beanMetaDataManager.getBeanMetaData( rootBeanClass ) It will traverse all of the initialization time metaDataProviders( By default, two , No, xml The way of ), Take out everything BeanConfiguration hand BeanMetaDataBuilder, And finally build one that belongs to this Bean Of BeanMetaData. Here is a description of the precautions :
Handle MetaDataProvider Called when ClassHierarchyHelper.getHierarchy( beanClass ) Method , It's not just about this kind of . After you get this class and all the parents , Give to provider.getBeanConfiguration( clazz ) Handle ( That is to say, any class will put Object Class handle once )

retrieveBeanConfiguration() details
This method makes sense , It's from Bean Search the properties inside 、 Method 、 Constructors need to be verified ConstrainedElement term .
private <T> BeanConfiguration<T> retrieveBeanConfiguration(Class<T> beanClass) {
// The scope of its search is :clazz.getDeclaredFields() What do you mean : Is to collect all fields of this class Include private wait But not all fields of the parent class
Set<ConstrainedElement> constrainedElements = getFieldMetaData( beanClass );
constrainedElements.addAll( getMethodMetaData( beanClass ) );
constrainedElements.addAll( getConstructorMetaData( beanClass ) );
//TODO GM: currently class level constraints are represented by a PropertyMetaData. This
//works but seems somewhat unnatural
// This TODO It is interesting to : At present , Class level constraints are defined by PropertyMetadata Express . This is feasible , But it seems a little unnatural
// ReturnValueMetaData、ExecutableMetaData、ParameterMetaData、PropertyMetaData
// In a word! : This is where class level validators are put in ( This set Most of the time it's empty )
Set<MetaConstraint<?>> classLevelConstraints = getClassLevelConstraints( beanClass );
if (!classLevelConstraints.isEmpty()) {
ConstrainedType classLevelMetaData = new ConstrainedType(ConfigurationSource.ANNOTATION, beanClass, classLevelConstraints);
constrainedElements.add(classLevelMetaData);
}
// Assemble into one BeanConfiguration return
return new BeanConfiguration<>(ConfigurationSource.ANNOTATION, beanClass,
constrainedElements,
getDefaultGroupSequence( beanClass ), // All of the @GroupSequence annotation
getDefaultGroupSequenceProvider( beanClass ) // All of the @GroupSequenceProvider annotation
);
}
This step puts the Bean Fields on 、 Methods and other items that need to be verified are extracted . Take... In the example above Demo check Person Category , The final result is BeanConfiguration as follows :( Two )


This is an intuitive conclusion , You can see that just a simple class actually contains a lot of items .
Here's a sentence : There are so many , But not everyone needs to go through the verification logic . Because after all, most items have no constraints ( annotation ), majority ConstrainedElement.getConstraints() It's empty
In general , My personal advice is not just to remember the conclusion , Because it's easy to forget , So we have to go a little deeper , Let the memory be more profound . Then go deep into the following four aspects :
retrieval Field:getFieldMetaData( beanClass )
- Get all the fields of this class Field:clazz.getDeclaredFields()
- Put each Field They're all packaged as ConstrainedElement Store it
- Be careful : This step completes for each Field The notes marked on are saved
retrieval Method:getMethodMetaData( beanClass ):
- Get all the methods in this class Method:clazz.getDeclaredMethods()
- Eliminate static methods and compositing (isSynthetic) Method
- Put each Method All converted into a ConstrainedExecutable With ~~(ConstrainedExecutable It's also a ConstrainedElement). In the meantime it did the following ( Methods and constructors are a little more complicated , Because it contains input and return values ):
- 1. Find all the comments on the method and save them
- 2. Process the input parameters 、 Return value ( Including whether automatic judgment is applied to input parameter or return value )
retrieval Constructor:getConstructorMetaData( beanClass ):
It's the same thing Method, A little
retrieval Type:getClassLevelConstraints( beanClass ):
- Find all the comments on this class , convert to ConstraintDescriptor
- Yes, we have found each one ConstraintDescriptor To deal with , In the end, they all switch Set<MetaConstraint<?>> This type
- hold Set<MetaConstraint<?>> Use one ConstrainedType Pack up (ConstrainedType It's a ConstrainedElement)
The metadata extraction of cascade verification is made by findCascadingMetaData Method to complete (@Valid Information is extracted here ), We are more concerned about the scenarios in which this method will be called , It also explains the scenarios in which cascading verification will take effect :
// type explain : They are as follows N Medium condition
// Field by :.getGenericType() // Type of field
// Method by :.getGenericReturnType() // return type
// Constructor:.getDeclaringClass() // The class of the constructor
// annotatedElement: It doesn't have to be annotated to get in ( Each field 、 Method 、 Constructors can be passed in )
private CascadingMetaDataBuilder getCascadingMetaData(Type type, AnnotatedElement annotatedElement, Map<TypeVariable<?>, CascadingMetaDataBuilder> containerElementTypesCascadingMetaData) {
return CascadingMetaDataBuilder.annotatedObject( type, annotatedElement.isAnnotationPresent( Valid.class ), containerElementTypesCascadingMetaData, getGroupConversions( annotatedElement ) );
}
findCascadingMetaData Methods are extracting object attribute metadata and methods , Constructor metadata extraction will be called .
validator.validate Analysis of method source code flow
Get metadata information , Prepare the context
- Parse the current object and get the metadata information of the object
@Override
public final <T> Set<ConstraintViolation<T>> validate(T object, Class<?>... groups) {
...
Class<T> rootBeanClass = (Class<T>) object.getClass();
BeanMetaData<T> rootBeanMetaData = beanMetaDataManager.getBeanMetaData( rootBeanClass );
// If the current object has no related constraints , Massage directly returns null
if ( !rootBeanMetaData.hasConstraints() ) {
return Collections.emptySet();
}
// Get ready validationContext and valueContext
BaseBeanValidationContext<T> validationContext = getValidationContextBuilder().forValidate( rootBeanClass, rootBeanMetaData, object );
ValidationOrder validationOrder = determineGroupValidationOrder( groups );
BeanValueContext<?, Object> valueContext = ValueContexts.getLocalExecutionContextForBean(
validatorScopedContext.getParameterNameProvider(),
object,
validationContext.getRootBeanMetaData(),
PathImpl.createRootPath()
);
// utilize validationContext and valueContext Finish right bean Object verification
return validateInContext( validationContext, valueContext, validationOrder );
}
validationContext The most important part is its internal management BeanMetaData, That is, the metadata information of the object .

valueContext More emphasis is placed on the relevant operations of obtaining and verifying object attribute values 
BeanMetaData It is the core of data verification , His structure is as follows :
BeanMetaData The constraint information related to the current object is recorded internally , And internal allMetaConstraints Constraint information is recorded in the array , Each in the array MetaConstraint Provided internally ConstraintTree Responsible for completing the specific verification logic :
validationOrder What is saved is that users need to verify several groups at the same time :
Set<ConstraintViolation<Person>> result = validator.validate(person, Person.Simple.class,Person.Complex.class);

Check one by one according to groups
- validateInContext utilize validationContext and valueContext The context information provided completes the data verification
private <T, U> Set<ConstraintViolation<T>> validateInContext(BaseBeanValidationContext<T> validationContext, BeanValueContext<U, Object> valueContext,
ValidationOrder validationOrder) {
...
BeanMetaData<U> beanMetaData = valueContext.getCurrentBeanMetaData();
...
// Check the groups that users need to check one by one
Iterator<Group> groupIterator = validationOrder.getGroupIterator();
while ( groupIterator.hasNext() ) {
Group group = groupIterator.next();
// You can guess. valueContext Be responsible for verifying the constraints belonging to the current group
valueContext.setCurrentGroup( group.getDefiningClass() );
// Carry out specific verification
validateConstraintsForCurrentGroup( validationContext, valueContext );
// If set failFast Mark as true , And there is an error in the current group verification , Then direct short circuit return
// The default is false
if ( shouldFailFast( validationContext ) ) {
return validationContext.getFailingConstraints();
}
}
// Then verify the cascade attribute of each group
groupIterator = validationOrder.getGroupIterator();
while ( groupIterator.hasNext() ) {
Group group = groupIterator.next();
valueContext.setCurrentGroup( group.getDefiningClass() );
validateCascadedConstraints( validationContext, valueContext );
if ( shouldFailFast( validationContext ) ) {
return validationContext.getFailingConstraints();
}
}
// This one is ignored for the time being --- No big problem
// now we process sequences. For sequences I have to traverse the object graph since I have to stop processing when an error occurs.
Iterator<Sequence> sequenceIterator = validationOrder.getSequenceIterator();
while ( sequenceIterator.hasNext() ) {
...
}
// Return the error result after the above verification
return validationContext.getFailingConstraints();
}
The function of setting fast failure is also reflected above :
HibernateValidatorConfiguration configure = Validation.byProvider(HibernateValidator.class).configure();
ValidatorFactory validatorFactory = configure.failFast(false).buildValidatorFactory();
Check the non cascading attributes of the current group
- validateConstraintsForCurrentGroup: Check the non cascading attributes of the current group
private void validateConstraintsForCurrentGroup(BaseBeanValidationContext<?> validationContext, BeanValueContext<?, Object> valueContext) {
// we are not validating the default group there is nothing special to consider. If we are validating the default
// group sequence we have to consider that a class in the hierarchy could redefine the default group sequence.
// Determine whether to verify the default grouping or user-defined grouping
if ( !valueContext.validatingDefault() ) {
validateConstraintsForNonDefaultGroup( validationContext, valueContext );
}
else {
validateConstraintsForDefaultGroup( validationContext, valueContext );
}
}
- validateConstraintsForNonDefaultGroup: Let's first look at the verification process of user-defined groups
private void validateConstraintsForNonDefaultGroup(BaseBeanValidationContext<?> validationContext, BeanValueContext<?, Object> valueContext) {
// check
validateMetaConstraints( validationContext, valueContext, valueContext.getCurrentBean(), valueContext.getCurrentBeanMetaData().getMetaConstraints() );
// Mark that the current object has been processed
validationContext.markCurrentBeanAsProcessed( valueContext );
}
- validateMetaConstraints: Yes MetaConstraints Collection , Every MetaConstraint check
In the face of bean When extracting metadata from objects , Each constraint on the current object will be extracted as a MetaConstraint
private void validateMetaConstraints(BaseBeanValidationContext<?> validationContext, ValueContext<?, Object> valueContext, Object parent,
Iterable<MetaConstraint<?>> constraints) {
// For each metaConstraint All are verified , Then decide whether to fail quickly
for ( MetaConstraint<?> metaConstraint : constraints ) {
validateMetaConstraint( validationContext, valueContext, parent, metaConstraint );
// If at present metaConstraint Verification failed , And fast failure is marked as true , Then skip the constraint verification directly
if ( shouldFailFast( validationContext ) ) {
break;
}
}
}
- validateMetaConstraint: For single MetaConstraint check
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 the group corresponding to the current constraint is not the current group , Then skip without processing , Of course, there are other filtering logic , But it doesn't matter
if ( isValidationRequired( validationContext, valueContext, metaConstraint ) ) {
// If it is to verify the attributes in the object , So here parent It is the object to which the attribute belongs
if ( parent != null ) {
// Advance the value of the current attribute in the object , Set to the corresponding valueContext preservation
//CurrentValidatedValue Indicates the attribute value that needs to be verified at present
valueContext.setCurrentValidatedValue( valueContext.getValue( parent, metaConstraint.getLocation() ) );
}
//metaConstraint Of validateConstraint Complete data verification ---valueContext It stores the value corresponding to the currently verified attribute
success = metaConstraint.validateConstraint( validationContext, valueContext );
// At present metaConstraint Marked has been processed
validationContext.markConstraintProcessed( valueContext.getCurrentBean(), valueContext.getPropertyPath(), metaConstraint );
}
// reset the value context to the state before this call
valueContext.resetValueState( originalValueState );
return success;
}
- metaConstraint.validateConstraint: Complete the current metaConstraint The logic of constraint verification is as follows :
public boolean validateConstraint(ValidationContext<?> validationContext, ValueContext<?, Object> valueContext) {
...
// The core
success = doValidateConstraint( validationContext, valueContext );
...
return success;
}
private boolean doValidateConstraint(ValidationContext<?> executionContext, ValueContext<?, ?> valueContext) {
// Current verification is field verification , Method validation , Or class level verification
valueContext.setConstraintLocationKind( getConstraintLocationKind() );
// utilize MetaConstraint Inside ConstraintTree Of validateConstraints Complete the final verification logic , If something goes wrong
// The error message will be put in validationContext in , So this right here is executionContext in
boolean validationResult = constraintTree.validateConstraints( executionContext, valueContext );
return validationResult;
}
So the core logic to complete data verification is MetaConstraint Inside constraintTree Of validateConstraints In the method
constraintTree Of validateConstraints Method to complete the final verification

At present, only a single constraint is involved in checking , Compound verification is not involved yet , therefore constraintTree The concrete realization of : SimpleConstraintTree
- The first is the parent class ConstraintTree Of validateConstraints Method :
public final boolean validateConstraints(ValidationContext<?> validationContext, ValueContext<?, ?> valueContext) {
// A collection of verification error messages , If the verification fails , Then the set is empty
List<ConstraintValidatorContextImpl> violatedConstraintValidatorContexts = new ArrayList<>( 5 );
// Call the implementation of the subclass , Complete the final verification logic
validateConstraints( validationContext, valueContext, violatedConstraintValidatorContexts );
// Judge whether the current constraint verification is successful or failed
if ( !violatedConstraintValidatorContexts.isEmpty() ) {
for ( ConstraintValidatorContextImpl constraintValidatorContext : violatedConstraintValidatorContexts ) {
for ( ConstraintViolationCreationContext constraintViolationCreationContext : constraintValidatorContext.getConstraintViolationCreationContexts() ) {
// Add failure error to validationContext
validationContext.addConstraintFailure(
valueContext, constraintViolationCreationContext, constraintValidatorContext.getConstraintDescriptor()
);
}
}
return false;
}
return true;
}
- SimpleConstraintTree Of validateConstraints Method to complete the real constraint verification logic
@Override
protected void validateConstraints(ValidationContext<?> validationContext,
ValueContext<?, ?> valueContext,
Collection<ConstraintValidatorContextImpl> violatedConstraintValidatorContexts) {
...
// find the right constraint validator
// Initialize the validator corresponding to the current constraint annotation
ConstraintValidator<B, ?> validator = getInitializedConstraintValidator( validationContext, valueContext );
// create a constraint validator context
// Constraint verifier context
ConstraintValidatorContextImpl constraintValidatorContext = validationContext.createConstraintValidatorContextFor(
descriptor, valueContext.getPropertyPath()
);
// validate
//validateSingleConstraint Complete single constraint verification , The return is optional object , If optional There are objects inside , The description is an error message
// Otherwise, the verification is successful , There is no wrong
if ( validateSingleConstraint( valueContext, constraintValidatorContext, validator ).isPresent() ) {
violatedConstraintValidatorContexts.add( constraintValidatorContext );
}
}
- validateSingleConstraint It can be seen that it should be the place where the mystery is revealed
protected final <V> Optional<ConstraintValidatorContextImpl> validateSingleConstraint(
ValueContext<?, ?> valueContext,
ConstraintValidatorContextImpl constraintValidatorContext,
ConstraintValidator<A, V> validator) {
boolean isValid;
try {
@SuppressWarnings("unchecked")
// from valueContext Take out the value that needs to be verified
V validatedValue = (V) valueContext.getCurrentValidatedValue();
// Calling the verifier isValid Method , The return value determines whether the verification is successful , The first parameter is the value that needs to be verified , The second parameter is the context
isValid = validator.isValid( validatedValue, constraintValidatorContext );
}
catch (RuntimeException e) {
if ( e instanceof ConstraintDeclarationException ) {
throw e;
}
throw LOG.getExceptionDuringIsValidCallException( e );
}
// If the verification fails , Will return the incoming constraintValidatorContext
if ( !isValid ) {
//We do not add these violations yet, since we don't know how they are
//going to influence the final boolean evaluation
return Optional.of( constraintValidatorContext );
}
// Verification succeeded , It returns an empty object
return Optional.empty();
}
Check the cascade attribute of the current group
After verifying the common attributes in the Group , Next, you need to verify the cascading attributes :
....
groupIterator = validationOrder.getGroupIterator();
while ( groupIterator.hasNext() ) {
Group group = groupIterator.next();
valueContext.setCurrentGroup( group.getDefiningClass() );
// Verify cascading attributes
validateCascadedConstraints( validationContext, valueContext );
if ( shouldFailFast( validationContext ) ) {
return validationContext.getFailingConstraints();
}
}
....
validateCascadedConstraints The core logic of is recursive verification ;
private void validateCascadedConstraints(BaseBeanValidationContext<?> validationContext, ValueContext<?, Object> valueContext) {
// here Validatable It's up there beanMetea Metadata
Validatable validatable = valueContext.getCurrentValidatable();
BeanValueContext.ValueState<Object> originalValueState = valueContext.getCurrentValueState() ;
// Get all annotations in the current object @Valid Cascading properties of annotations , Sequential processing
for ( Cascadable cascadable : validatable.getCascadables() ) {
...
// Get the value corresponding to the current cascade attribute
Object value = getCascadableValue( validationContext, valueContext.getCurrentBean(), cascadable );
// Get the metadata corresponding to the cascading attribute
CascadingMetaData cascadingMetaData = cascadable.getCascadingMetaData();
...
// The current cascading attribute is verified according to the group to which the current attribute belongs
validateCascadedAnnotatedObjectForCurrentGroup( value, validationContext, valueContext, effectiveCascadingMetaData );
...
}
}
validateCascadedAnnotatedObjectForCurrentGroup Here we are going to enter recursive verification :
private void validateCascadedAnnotatedObjectForCurrentGroup(Object value, BaseBeanValidationContext<?> validationContext, ValueContext<?, Object> valueContext,
CascadingMetaData cascadingMetaData) {
Class<?> originalGroup = valueContext.getCurrentGroup();
Class<?> currentGroup = cascadingMetaData.convertGroup( originalGroup );
...
//ValidationOrder The group saved in is the group to which the current cascade attribute belongs
ValidationOrder validationOrder = validationOrderGenerator.getValidationOrder( currentGroup, currentGroup != originalGroup );
// Build cascading attributes corresponding to ValueContext, and validationContext Use the same as my father
BeanValueContext<?, Object> cascadedValueContext = buildNewLocalExecutionContext( valueContext, value );
// Start recursion
validateInContext( validationContext, cascadedValueContext, validationOrder );
}
Summary
So far , We have basically validator Conduct validate The core source code of data verification has been roughly reviewed .
If you are still thinking about why a constraint annotation does not take effect , Or why the cascading attribute is not resolved , These problems need to look at the process of metadata information extraction , See if your comments have been detected , I didn't mention this part above , When you encounter problems , Go by yourself debug Source code .
Common constraint annotation explanation
All constraint annotations can be marked repeatedly , Because it has the following repeated marks on it :
@Repeatable(List.class)
java:@Repeatable Annotations use
JSR Standard notes :


explain :
@DecimalMax and @Max The difference between :
- @DecimalMax Support type :Number、BidDecimal、Float、Double、BigInteger、Long
- @Max Types of support : ditto
- Both of them can be marked on String On , such as “6” This string .( If you are not a numeric string , Never pass the verification )
All without special instructions :null is valid
If you use constraint annotations on unsupported types , Throw an exception at runtime :javax.validation.UnexpectedTypeException:No validator could be found for constraint ‘javax.validation.constraints.Future’ validating type ‘java.lang.String’
@FutureOrPresent and @PastOrPresent This note : about Present The matching of , Note that the program has execution time .so If it is a matching timestamp Instant, if Instant.now() Words ,@FutureOrPresent It's illegal , and @PastOrPresent It becomes legal . But if it's a date, for example LocalDate.now() There will be no such problem , After all, your program can't run for a day
@NotNull: Some people ask for basic types ( Is the non packing type wrong ?), Obviously, there will be no error . Because basic types have default values , It can't be null Of
All annotations can be marked on : Field 、 Method 、 Constructors 、 Enter the reference 、 And notes
JSR The comments of all statements are very simple , No, Hibernate The complexity of the offering , For example, it is not used @ReportAsSingleViolation Etc ~ For the sake of , The default prompt messages for each annotation are listed below ( chinese ):
javax.validation.constraints.AssertFalse.message = Only for false
javax.validation.constraints.AssertTrue.message = Only for true
javax.validation.constraints.DecimalMax.message = Must be less than or equal to {
value}
javax.validation.constraints.DecimalMin.message = Must be greater than or equal to {
value}
javax.validation.constraints.Digits.message = The value of the number is out of the allowed range ( Only in {
integer} Bit integers and {
fraction} In decimal places )
javax.validation.constraints.Email.message = Not a legal email address
javax.validation.constraints.Future.message = Need is a future time
javax.validation.constraints.FutureOrPresent.message = Need is a future or present time
javax.validation.constraints.Max.message = Maximum not exceeding {
value}
javax.validation.constraints.Min.message = The minimum cannot be less than {
value}
javax.validation.constraints.Negative.message = It has to be negative
javax.validation.constraints.NegativeOrZero.message = Must be negative or zero
javax.validation.constraints.NotBlank.message = Can't be empty
javax.validation.constraints.NotEmpty.message = Can't be empty
javax.validation.constraints.NotNull.message = Not for null
javax.validation.constraints.Null.message = It has to be for null
javax.validation.constraints.Past.message = Need is a past time
javax.validation.constraints.PastOrPresent.message = Need is a past or present time
javax.validation.constraints.Pattern.message = Need to match regular expression "{regexp}"
javax.validation.constraints.Positive.message = It must be a positive number
javax.validation.constraints.PositiveOrZero.message = Must be positive or zero
javax.validation.constraints.Size.message = The number must be in {
min} and {
max} Between
References ValidationMessages_zh_CN.properties, If the news doesn't suit you , It can be customized ~
Hibernate Validation Extended annotations

explain :
- @ReportAsSingleViolation: If @NotEmpty、@Pattern Both failed to verify , Do not add this annotation , Then two verification failure results will be generated . If this annotation is added , The error message is annotated with its annotation message Subject to
- All without special instructions :null is valid.
- All constraint annotations can be annotated repeatedly
The default prompt message for each annotation ( chinese ):
org.hibernate.validator.constraints.CreditCardNumber.message = Illegal credit card number
org.hibernate.validator.constraints.Currency.message = Illegal currency ( Must be {
value} One of them )
org.hibernate.validator.constraints.EAN.message = illegal {
type} Bar code
org.hibernate.validator.constraints.Email.message = Not a legal email address
org.hibernate.validator.constraints.Length.message = The length needs to be in {
min} and {
max} Between
org.hibernate.validator.constraints.CodePointLength.message = The length needs to be in {
min} and {
max} Between
org.hibernate.validator.constraints.LuhnCheck.message = ${
validatedValue} The check code of is illegal , Luhn model 10 Checksum mismatch
org.hibernate.validator.constraints.Mod10Check.message = ${
validatedValue} The check code of is illegal , model 10 Checksum mismatch
org.hibernate.validator.constraints.Mod11Check.message = ${
validatedValue} The check code of is illegal , model 11 Checksum mismatch
org.hibernate.validator.constraints.ModCheck.message = ${
validatedValue} The check code of is illegal , ${
modType} Checksum mismatch
org.hibernate.validator.constraints.NotBlank.message = Can't be empty
org.hibernate.validator.constraints.NotEmpty.message = Can't be empty
org.hibernate.validator.constraints.ParametersScriptAssert.message = Execute script expression "{script}" No return of expected results
org.hibernate.validator.constraints.Range.message = Need to be in {
min} and {
max} Between
org.hibernate.validator.constraints.SafeHtml.message = There may be unsafe HTML Content
org.hibernate.validator.constraints.ScriptAssert.message = Execute script expression "{script}" No return of expected results
org.hibernate.validator.constraints.URL.message = Need to be a legal URL
Used here $ { validatedValue }、$ { modType } yes EL The syntax of the expression .
@DurationMax and @DurationMin Of message The message is not posted here , A large number of EL Calculation , Is too long. ~~~
Reference resources
边栏推荐
- JS event object 2 e.charcode character code e.keycode key code box moves up, down, left and right
- openGauss源代码,用什么IDE工具管理、编辑、调试?
- Pycharm 快速给整页全部相同名称修改的快捷键
- unordered_map的hash function及hash bucket存储方式探索
- Canvas from getting started to persuading friends to give up (graphic version)
- tfx airflow 使用体验
- selenium+pytest+allure综合练习
- 没法预测明天的涨跌
- How to authenticate Youxuan database client
- [signal processing] weak signal detection in communication system based on the characteristics of high-order statistics with matlab code
猜你喜欢

数据中台建设(三):数据中台架构介绍

树的孩子兄弟表示法

LoRaWAN中的网关和chirpstack到底如何通信的?UDP?GRPC?MQTT?

How to simply realize the function of menu dragging and sorting
![[wechat applet development (VI)] draw the circular progress bar of the music player](/img/eb/9ce5d196970a6d6a887bf3e1d742ee.png)
[wechat applet development (VI)] draw the circular progress bar of the music player
![[image hiding] digital image information hiding system based on DCT, DWT, LHA, LSB, including various attacks and performance parameters, with matlab code](/img/69/1b547c35fd4af18405b586ad581da7.png)
[image hiding] digital image information hiding system based on DCT, DWT, LHA, LSB, including various attacks and performance parameters, with matlab code

CNN中的混淆矩阵 | PyTorch系列(二十三)

2022.7.8 supplement of empty Luna

【ELM分类】基于核极限学习机和极限学习机实现UCI数据集分类附matlab代码
![[image defogging] image defogging based on dark channel and non-mean filtering with matlab code](/img/39/6266eb14deac9f38b7e95f7291067e.png)
[image defogging] image defogging based on dark channel and non-mean filtering with matlab code
随机推荐
初识C语言 -- 操作符和关键字,#define,指针
One month's experience of joining Huawei OD
selenium+pytest+allure综合练习
Pychart shortcut key for quickly modifying all the same names on the whole page
POC模拟攻击利器 —— Nuclei入门(一)
Which users are suitable for applying for rapidssl certificate
【LeetCode】13. Linked List Cycle·环形链表
[image defogging] image defogging based on dark channel and non-mean filtering with matlab code
@Valid的作用(级联校验)以及常用约束注解的解释说明
How do gateways and chirpstacks in lorawan communicate? UDP? GRPC? MQTT?
Is the interface that can be seen everywhere in the program really useful? Is it really right?
Gbase8s how to delete data in a table with a foreign key relationship
Pycharm 快速给整页全部相同名称修改的快捷键
Newline required at end of file but not found.
Canvas 从入门到劝朋友放弃(图解版)
【微信小程序开发(五)】接口按照根据开发版体验版正式版智能配置
Flutter神操作学习之(满级攻略)
Common SQL statement query
Redis aof日志持久化
Four methods of modifying MySQL password (suitable for beginners)