В последнее время необходимо выполнить обработку отправки динамических данных, то есть необходимо проанализировать информацию об определении отправленных полей данных, прежде чем можно будет уточнить соответствующие конкретные типы полей, а затем выполнить преобразование типов данных и проверку достоверности полей, а затем отправить базу данных после бизнес-обработки и разработать набор самостоятельно.Логический цикл проверки слишком длинный, поэтому анализируется принцип реализации Spring Validation, и повторно используется валидатор с различными базовыми шаблонами.Здесь процесс анализа принцип Spring Validation будет записан, не вдаваясь в подробности.
Как использовать весеннюю валидацию
- Когда Spring Bean инициализируется, проверьте, соответствует ли компонент спецификации JSR-303.
1. Вручную добавьте BeanValidationPostProcessor Bean
2. Определите правила проверки в классе модели, такие как @Max, @Min, @NotEmpty.
3. Объявите Bean, полный код выглядит следующим образом:
@Bean
public BeanPostProcessor beanValidationPostProcessor() {
return new BeanValidationPostProcessor();
}
@Bean
public UserModel getUserModel() {
UserModel userModel = new UserModel();
userModel.setUsername(null);
userModel.setPassword("123");
return userModel;
}
@Data
class UserModel {
@NotNull(message = "username can not be null")
@Pattern(regexp = "[a-zA-Z0-9_]{5,10}", message = "username is illegal")
private String username;
@Size(min = 5, max = 10, message = "password's length is illegal")
private String password;
}
4. BeanValidationPostProcessor Bean имеет булев атрибут типа afterInitialization, по умолчанию false, если false, то bean проверяется в процессе postProcessBeforeInitialization, в противном случае bean проверяется в процессе postProcessAfterInitialization
5. Эта проверка использует логику Spring BeanPostProcessor, см.Одна из серий Spring Boot: Как быстро ознакомиться со стеком технологий Spring
6. Нижний уровень проверки вызывает метод doValidate, а затем вызывает validator.validate.Проверка по умолчанию — HibernateValidator, пакет validation-api — спецификация JAVA, а спецификация Spring по умолчанию реализована как пакет hibernate-validator. фреймворк без ORM Hibernate
protected void doValidate(Object bean) {
Assert.state(this.validator != null, "No Validator set");
Set<ConstraintViolation<Object>> result = this.validator.validate(bean);
7. HibernateValidator по умолчанию вызывает ValidatorFactoryImpl для создания валидатора и расширяет ValidatorFactoryImpl позже.
- Поддержка спецификации JSR-303 на уровне метода
1. Вручную добавьте компонент MethodValidationPostProcessor.
2. Добавьте в класс аннотацию @Validated (также поддерживает пользовательские аннотации, которые передаются при создании Bean-компонента MethodValidationPostProcessor)
3. Добавьте аннотации проверки к параметрам метода, такие как @Max, @Min, @NotEmpty, @NotNull и т. д., например
@Component
@Validated
public class BeanForMethodValidation {
public void validate(@NotEmpty String name, @Min(10) int age) {
System.out.println("validate, name: " + name + ", age: " + age);
}
}
4. MethodValidationPostProcessor использует aop внутри для завершения вызова метода.
public void afterPropertiesSet() {
Pointcut pointcut = new `AnnotationMatchingPointcut`(this.validatedAnnotationType, true);
this.advisor = new `DefaultPointcutAdvisor`(pointcut, createMethodValidationAdvice(this.validator));
}
protected Advice createMethodValidationAdvice(@Nullable Validator validator) {
return (validator != null ? new `MethodValidationInterceptor`(validator) : new MethodValidationInterceptor());
}
5. Нижний уровень также вызывает ValidatorFactoryImpl по умолчанию для создания валидатора, и проверка завершается валидатором.
- Прямое кодирование вызывает логику проверки, например
public class Person {
@NotNull(message = "性别不能为空")
private Gender gender;
@Min(10)
private Integer age;
...
}
ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();
Validator validator = validatorFactory.getValidator();
Person person = new Person();
person.setGender(Gender.Man);
validator.validate(person);
Как и выше, ValidatorFactoryImpl вызывается по умолчанию для создания валидатора, и валидатор выполняет определенную проверку.
- Использование в параметрах метода контроллера Spring
valid或validated
Аннотировать параметры для проверки
1. Ознакомьтесь с процессом вызова запроса Spring2. Вы можете видеть, что проверка параметров выполняется в процессе обработки параметров запроса различными резолверами.
3. Нижний слой единообразно вызывает метод проверки DataBinder.
4. Роль DataBinder: Биндер, позволяющий задавать значения свойств целевому объекту, включая поддержку валидации и анализа результатов привязки, то есть биндер обрабатывает параметры в виде строк, переданных запросом, и преобразует их в что действительно нужно серверу Тип, связующее обеспечивает поддержку проверки и может хранить результаты проверки
5. Валидатор DataBinder по умолчанию инициализируется в ConfigurableWebBindingInitializer, а по умолчанию используется OptionalValidatorFactoryBean. Этот компонент наследует LocalValidatorFactoryBean. получить валидатор
На данный момент все подсказки указывают на ValidatorFactoryImpl, следующий анализ этого класса
public Validator `getValidator`() {
return `createValidator`(
constraintValidatorManager.getDefaultConstraintValidatorFactory(),
valueExtractorManager,
validatorFactoryScopedContext,
methodValidationConfiguration
);
}
Validator `createValidator`(ConstraintValidatorFactory constraintValidatorFactory,
ValueExtractorManager valueExtractorManager,
ValidatorFactoryScopedContext validatorFactoryScopedContext,
MethodValidationConfiguration methodValidationConfiguration) {
BeanMetaDataManager beanMetaDataManager = beanMetaDataManagers.computeIfAbsent(
new BeanMetaDataManagerKey( validatorFactoryScopedContext.getParameterNameProvider(), valueExtractorManager, methodValidationConfiguration ),
key -> new BeanMetaDataManager(
`constraintHelper`,
executableHelper,
typeResolutionHelper,
validatorFactoryScopedContext.getParameterNameProvider(),
valueExtractorManager,
validationOrderGenerator,
buildDataProviders(),
methodValidationConfiguration
)
);
return `new ValidatorImpl`(
constraintValidatorFactory,
beanMetaDataManager,
valueExtractorManager,
constraintValidatorManager,
validationOrderGenerator,
validatorFactoryScopedContext
);
}
public final <T> Set<ConstraintViolation<T>> validate(T object, Class<?>... groups) {
Contracts.assertNotNull( object, MESSAGES.validatedObjectMustNotBeNull() );
sanityCheckGroups( groups );
ValidationContext<T> validationContext = `getValidationContextBuilder().forValidate( object )`;
if ( !validationContext.getRootBeanMetaData().hasConstraints() ) {
return Collections.emptySet();
}
ValidationOrder validationOrder = determineGroupValidationOrder( groups );
ValueContext<?, Object> valueContext = `ValueContext.getLocalExecutionContext`(
validatorScopedContext.getParameterNameProvider(),
object,
validationContext.getRootBeanMetaData(),
PathImpl.createRootPath()
);
return validateInContext( validationContext, valueContext, validationOrder );
}
1. getValidator->createValidator->ValidatorImpl->проверить
BeanMetaDataManager, validationContext, valueContext и другое содержимое инкапсулируются в процессе выполнения, и вся контекстная информация используется при проверке, например, все элементы проверки bean-компонента, подлежащего проверке (включая родительский класс и интерфейс), свойство, проверка параметров метода, информация о проверке. , различные классы инструментов, общие для валидатора, унаследованные от ValidatorFactoryScopedContext (такие как обработка сообщения, скрипт и т. д.) и т. д., содержимое более сложное
2. Проверка группы игнорируется и переходит к обработке группы по умолчанию.
3. Продолжайте вызывать метод doValidateConstraint MetaConstraint и переходите к разным деревьям ограничений в соответствии с разными типами аннотаций.
public static <U extends Annotation> ConstraintTree<U> of(ConstraintDescriptorImpl<U> composingDescriptor, Type validatedValueType) {
if ( composingDescriptor.getComposingConstraintImpls().isEmpty() ) {
return new SimpleConstraintTree<>( composingDescriptor, validatedValueType );
}
else {
return new ComposingConstraintTree<>( composingDescriptor, validatedValueType );
}
}
4. Игнорируйте, какие из них простые, а какие составные, потому что оба вызывают метод getInitializedConstraintValidator из ConstraintTree. Этот шаг используется для получения валидатора, соответствующего аннотации проверки (например, DecimalMax, NotEmpty и т. д.), и инициализации валидатор.
5.ConstraintHelper
Класс поддерживает все встроенные валидаторы и классифицирует их в соответствии с аннотацией проверки (например, DecimalMax) Класс описания валидатора поддерживает общий шаблон валидатора (например, BigDecimal) следующим образом:
putConstraints( tmpConstraints, DecimalMax.class, Arrays.asList(
DecimalMaxValidatorForBigDecimal.class,
DecimalMaxValidatorForBigInteger.class,
DecimalMaxValidatorForDouble.class,
DecimalMaxValidatorForFloat.class,
DecimalMaxValidatorForLong.class,
DecimalMaxValidatorForNumber.class,
DecimalMaxValidatorForCharSequence.class,
DecimalMaxValidatorForMonetaryAmount.class
) );
При получении валидатора конкретного класса бина сначала получить все валидаторы по аннотации, соответствующий метод ConstraintManager.findMatchingValidatorDescriptor, а затем получить единственный валидатор по типу проверяемого объекта
6. Затем инициализируйтеValidator в соответствии с контекстной информацией, а затем вызовите метод isValid валидатора для проверки