Lazy Validator resolution in MethodValidationPostProcessor

Closes gh-28990
This commit is contained in:
Juergen Hoeller
2022-08-22 14:07:42 +02:00
parent daf2d940e4
commit 9b2a40a3bc
2 changed files with 46 additions and 31 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,6 +18,7 @@ package org.springframework.validation.beanvalidation;
import java.lang.reflect.Method;
import java.util.Set;
import java.util.function.Supplier;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.ConstraintViolationException;
@@ -35,6 +36,7 @@ import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.function.SingletonSupplier;
import org.springframework.validation.annotation.Validated;
/**
@@ -60,14 +62,14 @@ import org.springframework.validation.annotation.Validated;
*/
public class MethodValidationInterceptor implements MethodInterceptor {
private final Validator validator;
private final Supplier<Validator> validator;
/**
* Create a new MethodValidationInterceptor using a default JSR-303 validator underneath.
*/
public MethodValidationInterceptor() {
this(Validation.buildDefaultValidatorFactory());
this.validator = SingletonSupplier.of(() -> Validation.buildDefaultValidatorFactory().getValidator());
}
/**
@@ -75,7 +77,7 @@ public class MethodValidationInterceptor implements MethodInterceptor {
* @param validatorFactory the JSR-303 ValidatorFactory to use
*/
public MethodValidationInterceptor(ValidatorFactory validatorFactory) {
this(validatorFactory.getValidator());
this.validator = SingletonSupplier.of(validatorFactory::getValidator);
}
/**
@@ -83,6 +85,16 @@ public class MethodValidationInterceptor implements MethodInterceptor {
* @param validator the JSR-303 Validator to use
*/
public MethodValidationInterceptor(Validator validator) {
this.validator = () -> validator;
}
/**
* Create a new MethodValidationInterceptor for the supplied
* (potentially lazily initialized) Validator.
* @param validator a Supplier for the Validator to use
* @since 6.0
*/
public MethodValidationInterceptor(Supplier<Validator> validator) {
this.validator = validator;
}
@@ -98,7 +110,7 @@ public class MethodValidationInterceptor implements MethodInterceptor {
Class<?>[] groups = determineValidationGroups(invocation);
// Standard Bean Validation 1.1 API
ExecutableValidator execVal = this.validator.forExecutables();
ExecutableValidator execVal = this.validator.get().forExecutables();
Method methodToValidate = invocation.getMethod();
Set<ConstraintViolation<Object>> result;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,7 +17,9 @@
package org.springframework.validation.beanvalidation;
import java.lang.annotation.Annotation;
import java.util.function.Supplier;
import jakarta.validation.Validation;
import jakarta.validation.Validator;
import jakarta.validation.ValidatorFactory;
import org.aopalliance.aop.Advice;
@@ -27,9 +29,10 @@ import org.springframework.aop.framework.autoproxy.AbstractBeanFactoryAwareAdvis
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.aop.support.annotation.AnnotationMatchingPointcut;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.function.SingletonSupplier;
import org.springframework.validation.annotation.Validated;
/**
@@ -62,8 +65,8 @@ public class MethodValidationPostProcessor extends AbstractBeanFactoryAwareAdvis
private Class<? extends Annotation> validatedAnnotationType = Validated.class;
@Nullable
private Validator validator;
private Supplier<Validator> validator = SingletonSupplier.of(() ->
Validation.buildDefaultValidatorFactory().getValidator());
/**
@@ -79,23 +82,6 @@ public class MethodValidationPostProcessor extends AbstractBeanFactoryAwareAdvis
this.validatedAnnotationType = validatedAnnotationType;
}
/**
* Set the JSR-303 Validator to delegate to for validating methods.
* <p>Default is the default ValidatorFactory's default Validator.
*/
public void setValidator(Validator validator) {
// Unwrap to the native Validator with forExecutables support
if (validator instanceof LocalValidatorFactoryBean) {
this.validator = ((LocalValidatorFactoryBean) validator).getValidator();
}
else if (validator instanceof SpringValidatorAdapter) {
this.validator = validator.unwrap(Validator.class);
}
else {
this.validator = validator;
}
}
/**
* Set the JSR-303 ValidatorFactory to delegate to for validating methods,
* using its default Validator.
@@ -103,7 +89,24 @@ public class MethodValidationPostProcessor extends AbstractBeanFactoryAwareAdvis
* @see jakarta.validation.ValidatorFactory#getValidator()
*/
public void setValidatorFactory(ValidatorFactory validatorFactory) {
this.validator = validatorFactory.getValidator();
this.validator = SingletonSupplier.of(validatorFactory::getValidator);
}
/**
* Set the JSR-303 Validator to delegate to for validating methods.
* <p>Default is the default ValidatorFactory's default Validator.
*/
public void setValidator(Validator validator) {
this.validator = () -> validator;
}
/**
* Set a lazily initialized Validator to delegate to for validating methods.
* @since 6.0
* @see #setValidator
*/
public void setValidatorProvider(ObjectProvider<Validator> validatorProvider) {
this.validator = validatorProvider::getObject;
}
@@ -116,13 +119,13 @@ public class MethodValidationPostProcessor extends AbstractBeanFactoryAwareAdvis
/**
* Create AOP advice for method validation purposes, to be applied
* with a pointcut for the specified 'validated' annotation.
* @param validator the JSR-303 Validator to delegate to
* @param validator a Supplier for the Validator to use
* @return the interceptor to use (typically, but not necessarily,
* a {@link MethodValidationInterceptor} or subclass thereof)
* @since 4.2
* @since 6.0
*/
protected Advice createMethodValidationAdvice(@Nullable Validator validator) {
return (validator != null ? new MethodValidationInterceptor(validator) : new MethodValidationInterceptor());
protected Advice createMethodValidationAdvice(Supplier<Validator> validator) {
return new MethodValidationInterceptor(validator);
}
}