diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/SpringValidatorAdapterWrapper.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/SpringValidatorAdapterWrapper.java deleted file mode 100644 index b44d770a493..00000000000 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/SpringValidatorAdapterWrapper.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright 2012-2017 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. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.autoconfigure.web; - -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.DisposableBean; -import org.springframework.beans.factory.InitializingBean; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextAware; -import org.springframework.validation.Errors; -import org.springframework.validation.Validator; -import org.springframework.validation.beanvalidation.SpringValidatorAdapter; - -/** - * Wraps a {@link SpringValidatorAdapter} so that only the Spring's {@link Validator} type - * is exposed. This prevents such a bean to expose both the Spring and JSR-303 validator - * contract at the same time. - * - * @author Stephane Nicoll - */ -class SpringValidatorAdapterWrapper - implements Validator, ApplicationContextAware, InitializingBean, DisposableBean { - - private final SpringValidatorAdapter target; - - private final boolean managed; - - SpringValidatorAdapterWrapper(SpringValidatorAdapter target, boolean managed) { - this.target = target; - this.managed = managed; - } - - public SpringValidatorAdapter getTarget() { - return this.target; - } - - @Override - public boolean supports(Class clazz) { - return this.target.supports(clazz); - } - - @Override - public void validate(Object target, Errors errors) { - this.target.validate(target, errors); - } - - @Override - public void setApplicationContext(ApplicationContext applicationContext) - throws BeansException { - if (!this.managed && this.target instanceof ApplicationContextAware) { - ((ApplicationContextAware) this.target) - .setApplicationContext(applicationContext); - } - } - - @Override - public void afterPropertiesSet() throws Exception { - if (!this.managed && this.target instanceof InitializingBean) { - ((InitializingBean) this.target).afterPropertiesSet(); - } - } - - @Override - public void destroy() throws Exception { - if (!this.managed && this.target instanceof DisposableBean) { - ((DisposableBean) this.target).destroy(); - } - } - -} diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/WebMvcAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/WebMvcAutoConfiguration.java index 53e537166a8..7b9f3222064 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/WebMvcAutoConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/WebMvcAutoConfiguration.java @@ -45,11 +45,9 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplicat import org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration; import org.springframework.boot.autoconfigure.web.ResourceProperties.Strategy; import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.boot.validation.MessageInterpolatorFactory; import org.springframework.boot.web.filter.OrderedHiddenHttpMethodFilter; import org.springframework.boot.web.filter.OrderedHttpPutFormContentFilter; import org.springframework.boot.web.filter.OrderedRequestContextFilter; -import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; @@ -69,8 +67,6 @@ import org.springframework.util.StringUtils; import org.springframework.validation.DefaultMessageCodesResolver; import org.springframework.validation.MessageCodesResolver; import org.springframework.validation.Validator; -import org.springframework.validation.beanvalidation.OptionalValidatorFactoryBean; -import org.springframework.validation.beanvalidation.SpringValidatorAdapter; import org.springframework.web.accept.ContentNegotiationManager; import org.springframework.web.bind.support.ConfigurableWebBindingInitializer; import org.springframework.web.context.request.RequestContextListener; @@ -133,8 +129,6 @@ public class WebMvcAutoConfiguration { public static String DEFAULT_SUFFIX = ""; - private static final String JSR303_VALIDATOR_CLASS = "javax.validation.Validator"; - @Bean @ConditionalOnMissingBean(HiddenHttpMethodFilter.class) public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter() { @@ -405,17 +399,12 @@ public class WebMvcAutoConfiguration { @Bean @Override public Validator mvcValidator() { - if (isJsr303Present()) { - Validator userDefinedValidator = getValidator(); - return new Jsr303ValidatorHandler(getApplicationContext(), - userDefinedValidator).wrapJsr303Validator(); + if (!ClassUtils.isPresent("javax.validation.Validator", + getClass().getClassLoader())) { + return super.mvcValidator(); } - return super.mvcValidator(); - } - - private boolean isJsr303Present() { - return ClassUtils.isPresent(JSR303_VALIDATOR_CLASS, - getApplicationContext().getClassLoader()); + return WebMvcValidator.get(getApplicationContext(), + getValidator()); } @Override @@ -559,62 +548,4 @@ public class WebMvcAutoConfiguration { } - static class Jsr303ValidatorHandler { - - private final ApplicationContext applicationContext; - - private final Validator userDefinedValidator; - - Jsr303ValidatorHandler(ApplicationContext applicationContext, - Validator userDefinedValidator) { - this.applicationContext = applicationContext; - this.userDefinedValidator = userDefinedValidator; - } - - public Validator wrapJsr303Validator() { - try { - if (this.userDefinedValidator != null) { - if (this.userDefinedValidator instanceof javax.validation.Validator) { - return wrap( - (javax.validation.Validator) this.userDefinedValidator, - false); - } - else { - return this.userDefinedValidator; - } - } - else { - return wrap(this.applicationContext - .getBean(javax.validation.Validator.class), true); - } - } - catch (NoSuchBeanDefinitionException ex) { - OptionalValidatorFactoryBean factory = new OptionalValidatorFactoryBean(); - MessageInterpolatorFactory interpolatorFactory = new MessageInterpolatorFactory(); - factory.setMessageInterpolator(interpolatorFactory.getObject()); - return new SpringValidatorAdapterWrapper(factory, false); - } - - } - - /** - * Wrap the specified {@code validator}. - * @param validator the validator to wrap - * @param bean {@code true} if the specified {@code validator} is a bean managed - * in the context - * @return a {@link Validator} wrapping the specified argument - */ - private Validator wrap(javax.validation.Validator validator, boolean bean) { - if (validator instanceof SpringValidatorAdapter) { - return new SpringValidatorAdapterWrapper( - (SpringValidatorAdapter) validator, bean); - } - else { - return new SpringValidatorAdapterWrapper( - new SpringValidatorAdapter(validator), false); - } - } - - } - } diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/WebMvcValidator.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/WebMvcValidator.java new file mode 100644 index 00000000000..499e08dbfdd --- /dev/null +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/WebMvcValidator.java @@ -0,0 +1,144 @@ +/* + * Copyright 2012-2017 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.autoconfigure.web; + +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.DisposableBean; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.boot.validation.MessageInterpolatorFactory; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.validation.Errors; +import org.springframework.validation.SmartValidator; +import org.springframework.validation.Validator; +import org.springframework.validation.beanvalidation.OptionalValidatorFactoryBean; +import org.springframework.validation.beanvalidation.SpringValidatorAdapter; + +/** + * A {@link SmartValidator} exposed as a bean for WebMvc use. Wraps existing + * {@link SpringValidatorAdapter} instances so that only the Spring's {@link Validator} + * type is exposed. This prevents such a bean to expose both the Spring and JSR-303 + * validator contract at the same time. + * + * @author Stephane Nicoll + * @author Phillip Webb + */ +class WebMvcValidator implements SmartValidator, ApplicationContextAware, + InitializingBean, DisposableBean { + + private final SpringValidatorAdapter target; + + private final boolean existingBean; + + WebMvcValidator(SpringValidatorAdapter target, boolean existingBean) { + this.target = target; + this.existingBean = existingBean; + } + + SpringValidatorAdapter getTarget() { + return this.target; + } + + @Override + public boolean supports(Class clazz) { + return this.target.supports(clazz); + } + + @Override + public void validate(Object target, Errors errors) { + this.target.validate(target, errors); + } + + @Override + public void validate(Object target, Errors errors, Object... validationHints) { + this.target.validate(target, errors, validationHints); + } + + @Override + public void setApplicationContext(ApplicationContext applicationContext) + throws BeansException { + if (!this.existingBean && this.target instanceof ApplicationContextAware) { + ((ApplicationContextAware) this.target) + .setApplicationContext(applicationContext); + } + } + + @Override + public void afterPropertiesSet() throws Exception { + if (!this.existingBean && this.target instanceof InitializingBean) { + ((InitializingBean) this.target).afterPropertiesSet(); + } + } + + @Override + public void destroy() throws Exception { + if (!this.existingBean && this.target instanceof DisposableBean) { + ((DisposableBean) this.target).destroy(); + } + } + + public static Validator get(ApplicationContext applicationContext, + Validator validator) { + if (validator != null) { + return wrap(validator, false); + } + return getExistingOrCreate(applicationContext); + } + + private static Validator getExistingOrCreate(ApplicationContext applicationContext) { + Validator existing = getExisting(applicationContext); + if (existing != null) { + return wrap(existing, true); + } + return create(); + } + + private static Validator getExisting(ApplicationContext applicationContext) { + try { + javax.validation.Validator validator = applicationContext + .getBean(javax.validation.Validator.class); + if (validator instanceof Validator) { + return (Validator) validator; + } + return new SpringValidatorAdapter(validator); + } + catch (NoSuchBeanDefinitionException ex) { + return null; + } + } + + private static Validator create() { + OptionalValidatorFactoryBean validator = new OptionalValidatorFactoryBean(); + validator.setMessageInterpolator(new MessageInterpolatorFactory().getObject()); + return wrap(validator, false); + } + + private static Validator wrap(Validator validator, boolean existingBean) { + if (validator instanceof javax.validation.Validator) { + if (validator instanceof SpringValidatorAdapter) { + return new WebMvcValidator((SpringValidatorAdapter) validator, + existingBean); + } + return new WebMvcValidator( + new SpringValidatorAdapter((javax.validation.Validator) validator), + existingBean); + } + return validator; + } + +} diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/WebMvcAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/WebMvcAutoConfigurationTests.java index dccaa88c309..bd6fcbab984 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/WebMvcAutoConfigurationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/WebMvcAutoConfigurationTests.java @@ -674,8 +674,8 @@ public class WebMvcAutoConfigurationTests { .isEmpty(); assertThat(this.context.getBeansOfType(Validator.class)).hasSize(1); Validator validator = this.context.getBean(Validator.class); - assertThat(validator).isInstanceOf(SpringValidatorAdapterWrapper.class); - assertThat(((SpringValidatorAdapterWrapper) validator).getTarget()) + assertThat(validator).isInstanceOf(WebMvcValidator.class); + assertThat(((WebMvcValidator) validator).getTarget()) .isSameAs(this.context.getBean(MvcJsr303Validator.class).validator); } @@ -687,8 +687,8 @@ public class WebMvcAutoConfigurationTests { .hasSize(1); assertThat(this.context.getBeansOfType(Validator.class)).hasSize(2); Validator validator = this.context.getBean("mvcValidator", Validator.class); - assertThat(validator).isInstanceOf(SpringValidatorAdapterWrapper.class); - assertThat(((SpringValidatorAdapterWrapper) validator).getTarget()) + assertThat(validator).isInstanceOf(WebMvcValidator.class); + assertThat(((WebMvcValidator) validator).getTarget()) .isSameAs(this.context.getBean(javax.validation.Validator.class)); } @@ -700,8 +700,8 @@ public class WebMvcAutoConfigurationTests { .hasSize(1); assertThat(this.context.getBeansOfType(Validator.class)).hasSize(1); Validator validator = this.context.getBean(Validator.class); - assertThat(validator).isInstanceOf(SpringValidatorAdapterWrapper.class); - SpringValidatorAdapter target = ((SpringValidatorAdapterWrapper) validator) + assertThat(validator).isInstanceOf(WebMvcValidator.class); + SpringValidatorAdapter target = ((WebMvcValidator) validator) .getTarget(); assertThat(new DirectFieldAccessor(target).getPropertyValue("targetValidator")) .isSameAs(this.context.getBean(javax.validation.Validator.class)); diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/SpringValidatorAdapterWrapperTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/WebMvcValidatorTests.java similarity index 85% rename from spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/SpringValidatorAdapterWrapperTests.java rename to spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/WebMvcValidatorTests.java index f85b5b53792..a0df0cf2393 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/SpringValidatorAdapterWrapperTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/WebMvcValidatorTests.java @@ -37,11 +37,11 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; /** - * Tests for {@link SpringValidatorAdapterWrapper}. + * Tests for {@link WebMvcValidator}. * * @author Stephane Nicoll */ -public class SpringValidatorAdapterWrapperTests { +public class WebMvcValidatorTests { private AnnotationConfigApplicationContext context; @@ -54,7 +54,7 @@ public class SpringValidatorAdapterWrapperTests { @Test public void wrapLocalValidatorFactoryBean() { - SpringValidatorAdapterWrapper wrapper = load( + WebMvcValidator wrapper = load( LocalValidatorFactoryBeanConfig.class); assertThat(wrapper.supports(SampleData.class)).isTrue(); MapBindingResult errors = new MapBindingResult(new HashMap(), @@ -89,12 +89,12 @@ public class SpringValidatorAdapterWrapperTests { verify(validator, times(0)).destroy(); } - private SpringValidatorAdapterWrapper load(Class config) { + private WebMvcValidator load(Class config) { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); ctx.register(config); ctx.refresh(); this.context = ctx; - return this.context.getBean(SpringValidatorAdapterWrapper.class); + return this.context.getBean(WebMvcValidator.class); } @Configuration @@ -106,8 +106,8 @@ public class SpringValidatorAdapterWrapperTests { } @Bean - public SpringValidatorAdapterWrapper wrapper() { - return new SpringValidatorAdapterWrapper(validator(), true); + public WebMvcValidator wrapper() { + return new WebMvcValidator(validator(), true); } } @@ -119,8 +119,8 @@ public class SpringValidatorAdapterWrapperTests { LocalValidatorFactoryBean.class); @Bean - public SpringValidatorAdapterWrapper wrapper() { - return new SpringValidatorAdapterWrapper(this.validator, false); + public WebMvcValidator wrapper() { + return new WebMvcValidator(this.validator, false); } } @@ -132,8 +132,8 @@ public class SpringValidatorAdapterWrapperTests { LocalValidatorFactoryBean.class); @Bean - public SpringValidatorAdapterWrapper wrapper() { - return new SpringValidatorAdapterWrapper(this.validator, true); + public WebMvcValidator wrapper() { + return new WebMvcValidator(this.validator, true); } }