From cb3d1befcd3f057c215ce206740ce616c20e041e Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 18 May 2017 21:00:56 +0200 Subject: [PATCH] LocalValidatorFactoryBean properly supports unwrap at ValidatorFactory level Also documents limitation for Bean Validation 2.0's getClockProvider() method. Issue: SPR-15561 Issue: SPR-13482 --- .../LocalValidatorFactoryBean.java | 34 +++++++++++++++++-- .../SpringValidatorAdapter.java | 7 ++-- .../SpringValidatorAdapterTests.java | 12 +++++-- .../beanvalidation/ValidatorFactoryTests.java | 17 +++++++++- 4 files changed, 63 insertions(+), 7 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/validation/beanvalidation/LocalValidatorFactoryBean.java b/spring-context/src/main/java/org/springframework/validation/beanvalidation/LocalValidatorFactoryBean.java index f9c2efc169..3a3a6d0a54 100644 --- a/spring-context/src/main/java/org/springframework/validation/beanvalidation/LocalValidatorFactoryBean.java +++ b/spring-context/src/main/java/org/springframework/validation/beanvalidation/LocalValidatorFactoryBean.java @@ -30,6 +30,7 @@ import javax.validation.MessageInterpolator; import javax.validation.ParameterNameProvider; import javax.validation.TraversableResolver; import javax.validation.Validation; +import javax.validation.ValidationException; import javax.validation.ValidationProviderResolver; import javax.validation.Validator; import javax.validation.ValidatorContext; @@ -64,8 +65,12 @@ import org.springframework.util.ReflectionUtils; * you will almost always use the default Validator anyway. This can also be injected directly * into any target dependency of type {@link org.springframework.validation.Validator}! * - *

As of Spring 5.0, this class requires Bean Validation 1.1, with special support + *

As of Spring 5.0, this class requires Bean Validation 1.1+, with special support * for Hibernate Validator 5.x (see {@link #setValidationMessageSource}). + * This class is also runtime-compatible with Bean Validation 2.0 and Hibernate Validator 6.0, + * with one special note: If you'd like to call BV 2.0's {@code getClockProvider()} method, + * obtain the native {@code ValidatorFactory} through {@code #unwrap(ValidatorFactory.class)} + * and call the {@code getClockProvider()} method on the returned native reference there. * *

This class is also being used by Spring's MVC configuration namespace, in case of the * {@code javax.validation} API being present but no explicit Validator having been configured. @@ -293,7 +298,6 @@ public class LocalValidatorFactoryBean extends SpringValidatorAdapter } private void configureParameterNameProviderIfPossible(Configuration configuration) { - // TODO: inner class final ParameterNameDiscoverer discoverer = this.parameterNameDiscoverer; final ParameterNameProvider defaultProvider = configuration.getDefaultParameterNameProvider(); configuration.parameterNameProvider(new ParameterNameProvider() { @@ -359,6 +363,32 @@ public class LocalValidatorFactoryBean extends SpringValidatorAdapter return this.validatorFactory.getParameterNameProvider(); } + // Bean Validation 2.0: currently not implemented here since it would imply + // a hard dependency on the new javax.validation.ClockProvider interface. + // To be resolved once Spring Framework requires Bean Validation 2.0+. + // Obtain the native ValidatorFactory through unwrap(ValidatorFactory.class) + // instead which will fully support a getClockProvider() call as well. + /* + @Override + public javax.validation.ClockProvider getClockProvider() { + Assert.notNull(this.validatorFactory, "No target ValidatorFactory set"); + return this.validatorFactory.getClockProvider(); + } + */ + + @Override + public T unwrap(Class type) { + if (type == null || !ValidatorFactory.class.isAssignableFrom(type)) { + try { + return super.unwrap(type); + } + catch (ValidationException ex) { + // ignore - we'll try ValidatorFactory unwrapping next + } + } + return this.validatorFactory.unwrap(type); + } + public void close() { if (this.validatorFactory != null) { this.validatorFactory.close(); diff --git a/spring-context/src/main/java/org/springframework/validation/beanvalidation/SpringValidatorAdapter.java b/spring-context/src/main/java/org/springframework/validation/beanvalidation/SpringValidatorAdapter.java index 4c418c4944..5ede486869 100644 --- a/spring-context/src/main/java/org/springframework/validation/beanvalidation/SpringValidatorAdapter.java +++ b/spring-context/src/main/java/org/springframework/validation/beanvalidation/SpringValidatorAdapter.java @@ -40,13 +40,16 @@ import org.springframework.validation.ObjectError; import org.springframework.validation.SmartValidator; /** - * Adapter that takes a JSR-303 {@code javax.validator.Validator} - * and exposes it as a Spring {@link org.springframework.validation.Validator} + * Adapter that takes a JSR-303 {@code javax.validator.Validator} and + * exposes it as a Spring {@link org.springframework.validation.Validator} * while also exposing the original JSR-303 Validator interface itself. * *

Can be used as a programmatic wrapper. Also serves as base class for * {@link CustomValidatorBean} and {@link LocalValidatorFactoryBean}. * + *

As of Spring Framework 5.0, this adapter is fully compatible with + * Bean Validation 1.1 as well as 2.0. + * * @author Juergen Hoeller * @since 3.0 */ diff --git a/spring-context/src/test/java/org/springframework/validation/beanvalidation/SpringValidatorAdapterTests.java b/spring-context/src/test/java/org/springframework/validation/beanvalidation/SpringValidatorAdapterTests.java index 5cd539f971..e63a8fe224 100644 --- a/spring-context/src/test/java/org/springframework/validation/beanvalidation/SpringValidatorAdapterTests.java +++ b/spring-context/src/test/java/org/springframework/validation/beanvalidation/SpringValidatorAdapterTests.java @@ -27,6 +27,7 @@ import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; import javax.validation.Payload; import javax.validation.Validation; +import javax.validation.Validator; import javax.validation.constraints.Pattern; import javax.validation.constraints.Size; @@ -51,8 +52,9 @@ import static org.junit.Assert.*; */ public class SpringValidatorAdapterTests { - private final SpringValidatorAdapter validatorAdapter = new SpringValidatorAdapter( - Validation.buildDefaultValidatorFactory().getValidator()); + private final Validator nativeValidator = Validation.buildDefaultValidatorFactory().getValidator(); + + private final SpringValidatorAdapter validatorAdapter = new SpringValidatorAdapter(nativeValidator); private final StaticMessageSource messageSource = new StaticMessageSource(); @@ -66,6 +68,12 @@ public class SpringValidatorAdapterTests { } + @Test + public void testUnwrap() { + Validator nativeValidator = validatorAdapter.unwrap(Validator.class); + assertSame(this.nativeValidator, nativeValidator); + } + @Test // SPR-13406 public void testNoStringArgumentValue() { TestBean testBean = new TestBean(); diff --git a/spring-context/src/test/java/org/springframework/validation/beanvalidation/ValidatorFactoryTests.java b/spring-context/src/test/java/org/springframework/validation/beanvalidation/ValidatorFactoryTests.java index d292e041cf..56aaee72e2 100644 --- a/spring-context/src/test/java/org/springframework/validation/beanvalidation/ValidatorFactoryTests.java +++ b/spring-context/src/test/java/org/springframework/validation/beanvalidation/ValidatorFactoryTests.java @@ -33,9 +33,12 @@ import javax.validation.ConstraintValidatorContext; import javax.validation.ConstraintViolation; import javax.validation.Payload; import javax.validation.Valid; +import javax.validation.Validator; +import javax.validation.ValidatorFactory; import javax.validation.constraints.NotNull; import org.hibernate.validator.HibernateValidator; +import org.hibernate.validator.HibernateValidatorFactory; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -75,6 +78,12 @@ public class ValidatorFactoryTests { fail("Invalid constraint violation with path '" + path + "'"); } } + + Validator nativeValidator = validator.unwrap(Validator.class); + assertTrue(nativeValidator.getClass().getName().startsWith("org.hibernate")); + assertTrue(validator.unwrap(ValidatorFactory.class) instanceof HibernateValidatorFactory); + assertTrue(validator.unwrap(HibernateValidatorFactory.class) instanceof HibernateValidatorFactory); + validator.destroy(); } @@ -96,6 +105,12 @@ public class ValidatorFactoryTests { fail("Invalid constraint violation with path '" + path + "'"); } } + + Validator nativeValidator = validator.unwrap(Validator.class); + assertTrue(nativeValidator.getClass().getName().startsWith("org.hibernate")); + assertTrue(validator.unwrap(ValidatorFactory.class) instanceof HibernateValidatorFactory); + assertTrue(validator.unwrap(HibernateValidatorFactory.class) instanceof HibernateValidatorFactory); + validator.destroy(); } @@ -465,7 +480,7 @@ public class ValidatorFactoryTests { String message() default "Should not be X"; - Class[] groups() default { }; + Class[] groups() default {}; Class[] payload() default {}; }