Skip to content

Commit cb3d1be

Browse files
committed
LocalValidatorFactoryBean properly supports unwrap at ValidatorFactory level
Also documents limitation for Bean Validation 2.0's getClockProvider() method. Issue: SPR-15561 Issue: SPR-13482
1 parent 67881a5 commit cb3d1be

File tree

4 files changed

+63
-7
lines changed

4 files changed

+63
-7
lines changed

Diff for: spring-context/src/main/java/org/springframework/validation/beanvalidation/LocalValidatorFactoryBean.java

+32-2
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import javax.validation.ParameterNameProvider;
3131
import javax.validation.TraversableResolver;
3232
import javax.validation.Validation;
33+
import javax.validation.ValidationException;
3334
import javax.validation.ValidationProviderResolver;
3435
import javax.validation.Validator;
3536
import javax.validation.ValidatorContext;
@@ -64,8 +65,12 @@
6465
* you will almost always use the default Validator anyway. This can also be injected directly
6566
* into any target dependency of type {@link org.springframework.validation.Validator}!
6667
*
67-
* <p><b>As of Spring 5.0, this class requires Bean Validation 1.1, with special support
68+
* <p><b>As of Spring 5.0, this class requires Bean Validation 1.1+, with special support
6869
* for Hibernate Validator 5.x</b> (see {@link #setValidationMessageSource}).
70+
* This class is also runtime-compatible with Bean Validation 2.0 and Hibernate Validator 6.0,
71+
* with one special note: If you'd like to call BV 2.0's {@code getClockProvider()} method,
72+
* obtain the native {@code ValidatorFactory} through {@code #unwrap(ValidatorFactory.class)}
73+
* and call the {@code getClockProvider()} method on the returned native reference there.
6974
*
7075
* <p>This class is also being used by Spring's MVC configuration namespace, in case of the
7176
* {@code javax.validation} API being present but no explicit Validator having been configured.
@@ -293,7 +298,6 @@ public void afterPropertiesSet() {
293298
}
294299

295300
private void configureParameterNameProviderIfPossible(Configuration<?> configuration) {
296-
// TODO: inner class
297301
final ParameterNameDiscoverer discoverer = this.parameterNameDiscoverer;
298302
final ParameterNameProvider defaultProvider = configuration.getDefaultParameterNameProvider();
299303
configuration.parameterNameProvider(new ParameterNameProvider() {
@@ -359,6 +363,32 @@ public ParameterNameProvider getParameterNameProvider() {
359363
return this.validatorFactory.getParameterNameProvider();
360364
}
361365

366+
// Bean Validation 2.0: currently not implemented here since it would imply
367+
// a hard dependency on the new javax.validation.ClockProvider interface.
368+
// To be resolved once Spring Framework requires Bean Validation 2.0+.
369+
// Obtain the native ValidatorFactory through unwrap(ValidatorFactory.class)
370+
// instead which will fully support a getClockProvider() call as well.
371+
/*
372+
@Override
373+
public javax.validation.ClockProvider getClockProvider() {
374+
Assert.notNull(this.validatorFactory, "No target ValidatorFactory set");
375+
return this.validatorFactory.getClockProvider();
376+
}
377+
*/
378+
379+
@Override
380+
public <T> T unwrap(Class<T> type) {
381+
if (type == null || !ValidatorFactory.class.isAssignableFrom(type)) {
382+
try {
383+
return super.unwrap(type);
384+
}
385+
catch (ValidationException ex) {
386+
// ignore - we'll try ValidatorFactory unwrapping next
387+
}
388+
}
389+
return this.validatorFactory.unwrap(type);
390+
}
391+
362392
public void close() {
363393
if (this.validatorFactory != null) {
364394
this.validatorFactory.close();

Diff for: spring-context/src/main/java/org/springframework/validation/beanvalidation/SpringValidatorAdapter.java

+5-2
Original file line numberDiff line numberDiff line change
@@ -40,13 +40,16 @@
4040
import org.springframework.validation.SmartValidator;
4141

4242
/**
43-
* Adapter that takes a JSR-303 {@code javax.validator.Validator}
44-
* and exposes it as a Spring {@link org.springframework.validation.Validator}
43+
* Adapter that takes a JSR-303 {@code javax.validator.Validator} and
44+
* exposes it as a Spring {@link org.springframework.validation.Validator}
4545
* while also exposing the original JSR-303 Validator interface itself.
4646
*
4747
* <p>Can be used as a programmatic wrapper. Also serves as base class for
4848
* {@link CustomValidatorBean} and {@link LocalValidatorFactoryBean}.
4949
*
50+
* <p>As of Spring Framework 5.0, this adapter is fully compatible with
51+
* Bean Validation 1.1 as well as 2.0.
52+
*
5053
* @author Juergen Hoeller
5154
* @since 3.0
5255
*/

Diff for: spring-context/src/test/java/org/springframework/validation/beanvalidation/SpringValidatorAdapterTests.java

+10-2
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import javax.validation.ConstraintValidatorContext;
2828
import javax.validation.Payload;
2929
import javax.validation.Validation;
30+
import javax.validation.Validator;
3031
import javax.validation.constraints.Pattern;
3132
import javax.validation.constraints.Size;
3233

@@ -51,8 +52,9 @@
5152
*/
5253
public class SpringValidatorAdapterTests {
5354

54-
private final SpringValidatorAdapter validatorAdapter = new SpringValidatorAdapter(
55-
Validation.buildDefaultValidatorFactory().getValidator());
55+
private final Validator nativeValidator = Validation.buildDefaultValidatorFactory().getValidator();
56+
57+
private final SpringValidatorAdapter validatorAdapter = new SpringValidatorAdapter(nativeValidator);
5658

5759
private final StaticMessageSource messageSource = new StaticMessageSource();
5860

@@ -66,6 +68,12 @@ public void setupSpringValidatorAdapter() {
6668
}
6769

6870

71+
@Test
72+
public void testUnwrap() {
73+
Validator nativeValidator = validatorAdapter.unwrap(Validator.class);
74+
assertSame(this.nativeValidator, nativeValidator);
75+
}
76+
6977
@Test // SPR-13406
7078
public void testNoStringArgumentValue() {
7179
TestBean testBean = new TestBean();

Diff for: spring-context/src/test/java/org/springframework/validation/beanvalidation/ValidatorFactoryTests.java

+16-1
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,12 @@
3333
import javax.validation.ConstraintViolation;
3434
import javax.validation.Payload;
3535
import javax.validation.Valid;
36+
import javax.validation.Validator;
37+
import javax.validation.ValidatorFactory;
3638
import javax.validation.constraints.NotNull;
3739

3840
import org.hibernate.validator.HibernateValidator;
41+
import org.hibernate.validator.HibernateValidatorFactory;
3942
import org.junit.Test;
4043

4144
import org.springframework.beans.factory.annotation.Autowired;
@@ -75,6 +78,12 @@ public void testSimpleValidation() throws Exception {
7578
fail("Invalid constraint violation with path '" + path + "'");
7679
}
7780
}
81+
82+
Validator nativeValidator = validator.unwrap(Validator.class);
83+
assertTrue(nativeValidator.getClass().getName().startsWith("org.hibernate"));
84+
assertTrue(validator.unwrap(ValidatorFactory.class) instanceof HibernateValidatorFactory);
85+
assertTrue(validator.unwrap(HibernateValidatorFactory.class) instanceof HibernateValidatorFactory);
86+
7887
validator.destroy();
7988
}
8089

@@ -96,6 +105,12 @@ public void testSimpleValidationWithCustomProvider() throws Exception {
96105
fail("Invalid constraint violation with path '" + path + "'");
97106
}
98107
}
108+
109+
Validator nativeValidator = validator.unwrap(Validator.class);
110+
assertTrue(nativeValidator.getClass().getName().startsWith("org.hibernate"));
111+
assertTrue(validator.unwrap(ValidatorFactory.class) instanceof HibernateValidatorFactory);
112+
assertTrue(validator.unwrap(HibernateValidatorFactory.class) instanceof HibernateValidatorFactory);
113+
99114
validator.destroy();
100115
}
101116

@@ -465,7 +480,7 @@ public List<String> getList() {
465480

466481
String message() default "Should not be X";
467482

468-
Class<?>[] groups() default { };
483+
Class<?>[] groups() default {};
469484

470485
Class<? extends Payload>[] payload() default {};
471486
}

0 commit comments

Comments
 (0)