Browse Source

Lazy Validator resolution in MethodValidationPostProcessor

Closes gh-28990
pull/28958/head
Juergen Hoeller 2 years ago
parent
commit
9b2a40a3bc
  1. 22
      spring-context/src/main/java/org/springframework/validation/beanvalidation/MethodValidationInterceptor.java
  2. 51
      spring-context/src/main/java/org/springframework/validation/beanvalidation/MethodValidationPostProcessor.java

22
spring-context/src/main/java/org/springframework/validation/beanvalidation/MethodValidationInterceptor.java

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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.lang.reflect.Method;
import java.util.Set; import java.util.Set;
import java.util.function.Supplier;
import jakarta.validation.ConstraintViolation; import jakarta.validation.ConstraintViolation;
import jakarta.validation.ConstraintViolationException; import jakarta.validation.ConstraintViolationException;
@ -35,6 +36,7 @@ import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.ClassUtils; import org.springframework.util.ClassUtils;
import org.springframework.util.function.SingletonSupplier;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
/** /**
@ -60,14 +62,14 @@ import org.springframework.validation.annotation.Validated;
*/ */
public class MethodValidationInterceptor implements MethodInterceptor { 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. * Create a new MethodValidationInterceptor using a default JSR-303 validator underneath.
*/ */
public MethodValidationInterceptor() { 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 * @param validatorFactory the JSR-303 ValidatorFactory to use
*/ */
public MethodValidationInterceptor(ValidatorFactory validatorFactory) { 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 * @param validator the JSR-303 Validator to use
*/ */
public MethodValidationInterceptor(Validator validator) { 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; this.validator = validator;
} }
@ -98,7 +110,7 @@ public class MethodValidationInterceptor implements MethodInterceptor {
Class<?>[] groups = determineValidationGroups(invocation); Class<?>[] groups = determineValidationGroups(invocation);
// Standard Bean Validation 1.1 API // Standard Bean Validation 1.1 API
ExecutableValidator execVal = this.validator.forExecutables(); ExecutableValidator execVal = this.validator.get().forExecutables();
Method methodToValidate = invocation.getMethod(); Method methodToValidate = invocation.getMethod();
Set<ConstraintViolation<Object>> result; Set<ConstraintViolation<Object>> result;

51
spring-context/src/main/java/org/springframework/validation/beanvalidation/MethodValidationPostProcessor.java

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

Loading…
Cancel
Save