從深處去掌握數據校驗@Valid的作用(級聯校驗)
- 2019 年 10 月 3 日
- 筆記
????
NBA???????????????????????
????
???Java??????????Java Bean Validation 2.0?JSR303?JSR349?JSR380?Hibernate-Validation 6.x????
???Spring??Controller????????????????Spring MVC??@Valid???JavaBean?????
???Spring?Spring?????????@Validated + MethodValidationPostProcessor???????????
??
??Bean Validation???????????????????????????????
??????Bean Validation????????????????????????????????Spring MVC?????????????????99%?????????????Spring MVC?Controller????????90%?????????@RequestBody????????JavaBean??~
???????Bean Validation??????????????????Spring????????????????????
?????????????????????????????????????????????????Spring????????~
????
?????????????????????????????????????????????????????????
@Getter @Setter @ToString public class Person { // ????message??????? @NotNull(message = "{message} -> ?????null", groups = Simple.class) public String name; @Max(value = 10, groups = Simple.class) @Positive(groups = Default.class) // ??????default public Integer age; @NotNull(groups = Complex.class) @NotEmpty(groups = Complex.class) private List<@Email String> emails; @Future(groups = Complex.class) private Date start; // ????? Simple??Complex? interface Simple { } interface Complex { } } ???????
public static void main(String[] args) { Person person = new Person(); //person.setName("fsx"); person.setAge(18); // email??????List?????? person.setEmails(Arrays.asList("[email protected]", "[email protected]", "aaa.com")); //person.setStart(new Date()); //start ??????????: Sun Jul 21 10:45:03 CST 2019 //person.setStart(new Date(System.currentTimeMillis() + 10000)); //???? HibernateValidatorConfiguration configure = Validation.byProvider(HibernateValidator.class).configure(); ValidatorFactory validatorFactory = configure.failFast(false).buildValidatorFactory(); // ??validatorFactory????Validator Validator validator = validatorFactory.getValidator(); // ???????????Default??Simple??Complex?? Set<ConstraintViolation<Person>> result = validator.validate(person, Person.Simple.class); //Set<ConstraintViolation<Person>> result = validator.validate(person, Person.Complex.class); // ????????? result.stream().map(v -> v.getPropertyPath() + " " + v.getMessage() + ": " + v.getInvalidValue()) .forEach(System.out::println); } ?????
age ??????10: 18 name {message} -> ?????null -> ?????null: null ??????????????????Person.Simple.class??Group?????~
?????Spring MVC????????????????????????
javax.validation.Valid????????????org.springframework.validation.annotation.Validated???????????????????
@Valid??
????JSR?????@Valid???????????????????????Controller?????@RequestBody??????????????????????????~
==?????????????????????????==
????????????????????????????????????????????????
???????@Valid???????????????
MetaDataProvider
???????????????????????????Provider???????????
- ??????????xml????????????? ??????????????
public enum ConfigurationSource { ANNOTATION( 0 ), XML( 1 ), API( 2 ); //programmatic API } MetaDataProvider???????????????- ?????????????????
?????@Valid??????????
public interface MetaDataProvider { // ?**??????**????Provider??? ?????????AnnotationProcessingOptionsImpl // ????????areMemberConstraintsIgnoredFor areReturnValueConstraintsIgnoredFor // ??????????????~~~~~~(?????) AnnotationProcessingOptions getAnnotationProcessingOptions(); // ??????Bean???`BeanConfiguration` ??????null? // BeanConfiguration??ConfigurationSource???~ <T> BeanConfiguration<? super T> getBeanConfiguration(Class<T> beanClass); } // ??????ConfigurationSource???Java???????????? ???????????????? // ?????????????????????? public class BeanConfiguration<T> { // ??????? private final ConfigurationSource source; private final Class<T> beanClass; // ConstrainedElement?????????????????????? // ConstrainedField/ConstrainedType/ConstrainedParameter/ConstrainedExecutable // ???ConstrainedExecutable????java.lang.reflect.Executable?? //???????java.lang.reflect.Method?Constructor private final Set<ConstrainedElement> constrainedElements; private final List<Class<?>> defaultGroupSequence; private final DefaultGroupSequenceProvider<? super T> defaultGroupSequenceProvider; ... // ????????????????????????? } ??????

??????????????????????????????????????AnnotationMetaDataProvider
AnnotationMetaDataProvider
???????????????????Hibernate Validation???configuration source??????????@Valid???~
public class AnnotationMetaDataProvider implements MetaDataProvider { private final ConstraintHelper constraintHelper; private final TypeResolutionHelper typeResolutionHelper; private final AnnotationProcessingOptions annotationProcessingOptions; private final ValueExtractorManager valueExtractorManager; // ???????????????????Bean ???????Bean??~~~ private final BeanConfiguration<Object> objectBeanConfiguration; // ?????? public AnnotationMetaDataProvider(ConstraintHelper constraintHelper, TypeResolutionHelper typeResolutionHelper, ValueExtractorManager valueExtractorManager, AnnotationProcessingOptions annotationProcessingOptions) { this.constraintHelper = constraintHelper; this.typeResolutionHelper = typeResolutionHelper; this.valueExtractorManager = valueExtractorManager; this.annotationProcessingOptions = annotationProcessingOptions; // ?????????Object?????????retrieve:?????? ????????~~~ // ???????????? this.objectBeanConfiguration = retrieveBeanConfiguration( Object.class ); } // ?????? @Override public AnnotationProcessingOptions getAnnotationProcessingOptions() { return new AnnotationProcessingOptionsImpl(); } // ????Bean?Object ??????~~~??????? ??Object? @Override @SuppressWarnings("unchecked") public <T> BeanConfiguration<T> getBeanConfiguration(Class<T> beanClass) { if ( Object.class.equals( beanClass ) ) { return (BeanConfiguration<T>) objectBeanConfiguration; } return retrieveBeanConfiguration( beanClass ); } } ????????????retrieveBeanConfiguration()???????????????????????????????????????
ValidatorFactory.getValidator()????????????????new??????????

- ??
Validator.validate()??????beanMetaDataManager.getBeanMetaData( rootBeanClass )???????????metaDataProviders(??????????xml???)??????BeanConfiguration??BeanMetaDataBuilder???????????Bean?BeanMetaData???????????????
1. ??MetaDataProvider????ClassHierarchyHelper.getHierarchy( beanClass )????????????????????????????provider.getBeanConfiguration( clazz )???????????????Object??????

retrieveBeanConfiguration()??
???????????Bean????????????????????ConstrainedElement??
private <T> BeanConfiguration<T> retrieveBeanConfiguration(Class<T> beanClass) { // ????????clazz.getDeclaredFields() ????????????????? ??private?? ???????????? 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 // ??TODO?????????????PropertyMetadata????????????????? // ReturnValueMetaData?ExecutableMetaData?ParameterMetaData?PropertyMetaData // ???????????????????????set?????????? Set<MetaConstraint<?>> classLevelConstraints = getClassLevelConstraints( beanClass ); if (!classLevelConstraints.isEmpty()) { ConstrainedType classLevelMetaData = new ConstrainedType(ConfigurationSource.ANNOTATION, beanClass, classLevelConstraints); constrainedElements.add(classLevelMetaData); } // ?????BeanConfiguration?? return new BeanConfiguration<>(ConfigurationSource.ANNOTATION, beanClass, constrainedElements, getDefaultGroupSequence( beanClass ), //????????@GroupSequence?? getDefaultGroupSequenceProvider( beanClass ) // ????????@GroupSequenceProvider?? ); } ??????Bean???????????????????????????Demo??Person?????????BeanConfiguration???????


?????????????????????????????????
??????????????????????????????????????????????????????
ConstrainedElement.getConstraints()???~
????????????????????????????????????????????????????????????
??Field?getFieldMetaData( beanClass )
- ????????
Field?clazz.getDeclaredFields() - ???
Field????ConstrainedElement????~~~
1. ????????????Field???????????
??Method?getMethodMetaData( beanClass )
- ?????????
Method?clazz.getDeclaredMethods() - ??????????(isSynthetic)??
???Method??????
ConstrainedExecutable??~~(ConstrainedExecutable???ConstrainedElement)????????????????????????????????????
1. ??????????????
2. ??????????????????????????????Constructor?getConstructorMetaData( beanClass )
?????Method??
??Type?getClassLevelConstraints( beanClass )
- ??????????????????
ConstraintDescriptor - ???????
ConstraintDescriptor??????????Set<MetaConstraint<?>>????
1. ?
Set<MetaConstraint<?>>???ConstrainedType?????ConstrainedType??ConstrainedElement?
==?????????????????Type?????????????????????==
??????????@Valid???????
// type??????N??? // Field??.getGenericType() // ????? // Method??.getGenericReturnType() // ????? // Constructor?.getDeclaringClass() // ?????? // annotatedElement??????????????????????????????????? private CascadingMetaDataBuilder getCascadingMetaData(Type type, AnnotatedElement annotatedElement, Map<TypeVariable<?>, CascadingMetaDataBuilder> containerElementTypesCascadingMetaData) { return CascadingMetaDataBuilder.annotatedObject( type, annotatedElement.isAnnotationPresent( Valid.class ), containerElementTypesCascadingMetaData, getGroupConversions( annotatedElement ) ); } ???????????????????annotatedElement.isAnnotationPresent(Valid.class)???????????????????????????????????JSR??@Valid???~
Spring????????????Spring??????~
ConstraintValidator.isValid()???
????????????????????ConstraintValidator.isValid()?????????????(??)?????(???)?
public abstract class ConstraintTree<A extends Annotation> { ... protected final <T, V> Set<ConstraintViolation<T>> validateSingleConstraint(ValidationContext<T> executionContext, ValueContext<?, ?> valueContext, ConstraintValidatorContextImpl constraintValidatorContext, ConstraintValidator<A, V> validator) { ... V validatedValue = (V) valueContext.getCurrentValidatedValue(); isValid = validator.isValid( validatedValue, constraintValidatorContext ); ... // ?????????????? ??????? if ( !isValid ) { return executionContext.createConstraintViolations(valueContext, constraintValidatorContext); } return Collections.emptySet(); } ... } ??????????????Group???
success = metaConstraint.validateConstraint( validationContext, valueContext ); MetaConstraint????????????????????ConstrainedElement.getConstraints??????????????????
// ConstraintTree<A> boolean validationResult = constraintTree.validateConstraints( executionContext, valueContext ); so????????isValid????????????
==??????????????????show????==
Demo Show
?????????Person??JavaBean???????????????????Field????????????Bean Validation?????????????????????????????
????Field
?
??Method??????
??Constructor??????
??????????????
?????????????????????????????????Spring??Controller????????????????Spring MVC??@Valid???JavaBean?????
????
?????????????????????????????????????????????????????????Person???????Child???????????Person??????????Child???????
@Getter @Setter @ToString public class Person { @NotNull private String name; @NotNull @Positive private Integer age; @Valid @NotNull private InnerChild child; @Getter @Setter @ToString public static class InnerChild { @NotNull private String name; @NotNull @Positive private Integer age; } } ???????
public static void main(String[] args) { Person person = new Person(); person.setName("fsx"); Person.InnerChild child = new Person.InnerChild(); child.setName("fsx-son"); child.setAge(-1); person.setChild(child); // ??? Validator validator = Validation.byProvider(HibernateValidator.class).configure().failFast(false) .buildValidatorFactory().getValidator(); Set<ConstraintViolation<Person>> result = validator.validate(person); // ?????? result.stream().map(v -> v.getPropertyPath() + " " + v.getMessage() + ": " + v.getInvalidValue()) .forEach(System.out::println); } ???
child.age ?????: -1 age ???null: null ?child.age??????????~
??
???????????????Bean Validation??????????????????????????Spring??????????~
????
????????????????-????-????-????-????
==The last????????????????????????????????????????????????~==
?????????????wx????Java??????3??
??????????wx??fsx641385712???????wx??????????"java??" ??????????