Reuse auto-configured `Validator` in WebFlux
This commit makes sure that if a `Validator` is auto-configured, it is reused as the `webFluxValidator`. This is pretty much the same thing as what was done recently for Spring MVC. Since the infrastructure is now shared, the package protected class has been published in the `.validation` package. Closes gh-8400
This commit is contained in:
parent
deaa6089b0
commit
cf64d9fd35
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.autoconfigure.web;
|
||||
package org.springframework.boot.autoconfigure.validation;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
|
@ -38,19 +38,19 @@ import org.springframework.validation.beanvalidation.SpringValidatorAdapter;
|
|||
* @author Stephane Nicoll
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class WebMvcValidator implements SmartValidator, ApplicationContextAware,
|
||||
public class SpringValidator implements SmartValidator, ApplicationContextAware,
|
||||
InitializingBean, DisposableBean {
|
||||
|
||||
private final SpringValidatorAdapter target;
|
||||
|
||||
private final boolean existingBean;
|
||||
|
||||
WebMvcValidator(SpringValidatorAdapter target, boolean existingBean) {
|
||||
public SpringValidator(SpringValidatorAdapter target, boolean existingBean) {
|
||||
this.target = target;
|
||||
this.existingBean = existingBean;
|
||||
}
|
||||
|
||||
SpringValidatorAdapter getTarget() {
|
||||
public final SpringValidatorAdapter getTarget() {
|
||||
return this.target;
|
||||
}
|
||||
|
||||
|
@ -131,10 +131,10 @@ class WebMvcValidator implements SmartValidator, ApplicationContextAware,
|
|||
private static Validator wrap(Validator validator, boolean existingBean) {
|
||||
if (validator instanceof javax.validation.Validator) {
|
||||
if (validator instanceof SpringValidatorAdapter) {
|
||||
return new WebMvcValidator((SpringValidatorAdapter) validator,
|
||||
return new SpringValidator((SpringValidatorAdapter) validator,
|
||||
existingBean);
|
||||
}
|
||||
return new WebMvcValidator(
|
||||
return new SpringValidator(
|
||||
new SpringValidatorAdapter((javax.validation.Validator) validator),
|
||||
existingBean);
|
||||
}
|
|
@ -43,6 +43,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
|
|||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
|
||||
import org.springframework.boot.autoconfigure.validation.SpringValidator;
|
||||
import org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.web.ResourceProperties.Strategy;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
|
@ -404,7 +405,7 @@ public class WebMvcAutoConfiguration {
|
|||
getClass().getClassLoader())) {
|
||||
return super.mvcValidator();
|
||||
}
|
||||
return WebMvcValidator.get(getApplicationContext(),
|
||||
return SpringValidator.get(getApplicationContext(),
|
||||
getValidator());
|
||||
}
|
||||
|
||||
|
|
|
@ -32,6 +32,7 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
|||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||
import org.springframework.boot.autoconfigure.validation.SpringValidator;
|
||||
import org.springframework.boot.autoconfigure.web.ConditionalOnEnabledResourceChain;
|
||||
import org.springframework.boot.autoconfigure.web.ResourceProperties;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
|
@ -45,6 +46,8 @@ import org.springframework.core.convert.converter.GenericConverter;
|
|||
import org.springframework.format.Formatter;
|
||||
import org.springframework.format.FormatterRegistry;
|
||||
import org.springframework.http.CacheControl;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.validation.Validator;
|
||||
import org.springframework.web.reactive.config.DelegatingWebFluxConfiguration;
|
||||
import org.springframework.web.reactive.config.EnableWebFlux;
|
||||
import org.springframework.web.reactive.config.ResourceChainRegistration;
|
||||
|
@ -71,15 +74,14 @@ import org.springframework.web.reactive.result.view.ViewResolver;
|
|||
@Configuration
|
||||
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
|
||||
@ConditionalOnClass(WebFluxConfigurer.class)
|
||||
@ConditionalOnMissingBean(RouterFunction.class)
|
||||
@ConditionalOnMissingBean({ WebFluxConfigurationSupport.class, RouterFunction.class })
|
||||
@AutoConfigureAfter(ReactiveWebServerAutoConfiguration.class)
|
||||
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
|
||||
public class WebFluxAnnotationAutoConfiguration {
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnMissingBean(WebFluxConfigurationSupport.class)
|
||||
@EnableConfigurationProperties({ResourceProperties.class, WebFluxProperties.class})
|
||||
@Import(DelegatingWebFluxConfiguration.class)
|
||||
@Import(EnableWebFluxConfiguration.class)
|
||||
public static class WebFluxConfig implements WebFluxConfigurer {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(WebFluxConfig.class);
|
||||
|
@ -178,6 +180,26 @@ public class WebFluxAnnotationAutoConfiguration {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Configuration equivalent to {@code @EnableWebFlux}.
|
||||
*/
|
||||
@Configuration
|
||||
public static class EnableWebFluxConfiguration
|
||||
extends DelegatingWebFluxConfiguration {
|
||||
|
||||
@Override
|
||||
@Bean
|
||||
public Validator webFluxValidator() {
|
||||
if (!ClassUtils.isPresent("javax.validation.Validator",
|
||||
getClass().getClassLoader())) {
|
||||
return super.webFluxValidator();
|
||||
}
|
||||
return SpringValidator.get(getApplicationContext(),
|
||||
getValidator());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnEnabledResourceChain
|
||||
static class ResourceChainCustomizerConfiguration {
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.autoconfigure.web;
|
||||
package org.springframework.boot.autoconfigure.validation;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
|
@ -37,11 +37,11 @@ import static org.mockito.Mockito.times;
|
|||
import static org.mockito.Mockito.verify;
|
||||
|
||||
/**
|
||||
* Tests for {@link WebMvcValidator}.
|
||||
* Tests for {@link SpringValidator}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class WebMvcValidatorTests {
|
||||
public class SpringValidatorTests {
|
||||
|
||||
private AnnotationConfigApplicationContext context;
|
||||
|
||||
|
@ -54,7 +54,7 @@ public class WebMvcValidatorTests {
|
|||
|
||||
@Test
|
||||
public void wrapLocalValidatorFactoryBean() {
|
||||
WebMvcValidator wrapper = load(
|
||||
SpringValidator wrapper = load(
|
||||
LocalValidatorFactoryBeanConfig.class);
|
||||
assertThat(wrapper.supports(SampleData.class)).isTrue();
|
||||
MapBindingResult errors = new MapBindingResult(new HashMap<String, Object>(),
|
||||
|
@ -89,12 +89,12 @@ public class WebMvcValidatorTests {
|
|||
verify(validator, times(0)).destroy();
|
||||
}
|
||||
|
||||
private WebMvcValidator load(Class<?> config) {
|
||||
private SpringValidator load(Class<?> config) {
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
|
||||
ctx.register(config);
|
||||
ctx.refresh();
|
||||
this.context = ctx;
|
||||
return this.context.getBean(WebMvcValidator.class);
|
||||
return this.context.getBean(SpringValidator.class);
|
||||
}
|
||||
|
||||
@Configuration
|
||||
|
@ -106,8 +106,8 @@ public class WebMvcValidatorTests {
|
|||
}
|
||||
|
||||
@Bean
|
||||
public WebMvcValidator wrapper() {
|
||||
return new WebMvcValidator(validator(), true);
|
||||
public SpringValidator wrapper() {
|
||||
return new SpringValidator(validator(), true);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -119,8 +119,8 @@ public class WebMvcValidatorTests {
|
|||
LocalValidatorFactoryBean.class);
|
||||
|
||||
@Bean
|
||||
public WebMvcValidator wrapper() {
|
||||
return new WebMvcValidator(this.validator, false);
|
||||
public SpringValidator wrapper() {
|
||||
return new SpringValidator(this.validator, false);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -132,8 +132,8 @@ public class WebMvcValidatorTests {
|
|||
LocalValidatorFactoryBean.class);
|
||||
|
||||
@Bean
|
||||
public WebMvcValidator wrapper() {
|
||||
return new WebMvcValidator(this.validator, true);
|
||||
public SpringValidator wrapper() {
|
||||
return new SpringValidator(this.validator, true);
|
||||
}
|
||||
|
||||
}
|
|
@ -39,6 +39,7 @@ import org.junit.rules.ExpectedException;
|
|||
import org.springframework.beans.DirectFieldAccessor;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.validation.SpringValidator;
|
||||
import org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration.WebMvcAutoConfigurationAdapter;
|
||||
import org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration.WelcomePageHandlerMapping;
|
||||
import org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext;
|
||||
|
@ -674,8 +675,8 @@ public class WebMvcAutoConfigurationTests {
|
|||
.isEmpty();
|
||||
assertThat(this.context.getBeansOfType(Validator.class)).hasSize(1);
|
||||
Validator validator = this.context.getBean(Validator.class);
|
||||
assertThat(validator).isInstanceOf(WebMvcValidator.class);
|
||||
assertThat(((WebMvcValidator) validator).getTarget())
|
||||
assertThat(validator).isInstanceOf(SpringValidator.class);
|
||||
assertThat(((SpringValidator) validator).getTarget())
|
||||
.isSameAs(this.context.getBean(MvcJsr303Validator.class).validator);
|
||||
}
|
||||
|
||||
|
@ -687,8 +688,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(WebMvcValidator.class);
|
||||
assertThat(((WebMvcValidator) validator).getTarget())
|
||||
assertThat(validator).isInstanceOf(SpringValidator.class);
|
||||
assertThat(((SpringValidator) validator).getTarget())
|
||||
.isSameAs(this.context.getBean(javax.validation.Validator.class));
|
||||
}
|
||||
|
||||
|
@ -700,8 +701,8 @@ public class WebMvcAutoConfigurationTests {
|
|||
.hasSize(1);
|
||||
assertThat(this.context.getBeansOfType(Validator.class)).hasSize(1);
|
||||
Validator validator = this.context.getBean(Validator.class);
|
||||
assertThat(validator).isInstanceOf(WebMvcValidator.class);
|
||||
SpringValidatorAdapter target = ((WebMvcValidator) validator)
|
||||
assertThat(validator).isInstanceOf(SpringValidator.class);
|
||||
SpringValidatorAdapter target = ((SpringValidator) validator)
|
||||
.getTarget();
|
||||
assertThat(new DirectFieldAccessor(target).getPropertyValue("targetValidator"))
|
||||
.isSameAs(this.context.getBean(javax.validation.Validator.class));
|
||||
|
|
|
@ -16,8 +16,14 @@
|
|||
|
||||
package org.springframework.boot.autoconfigure.webflux;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import javax.validation.ValidatorFactory;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.beans.DirectFieldAccessor;
|
||||
import org.springframework.boot.autoconfigure.validation.SpringValidator;
|
||||
import org.springframework.boot.context.GenericReactiveWebApplicationContext;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.test.util.EnvironmentTestUtils;
|
||||
|
@ -28,9 +34,13 @@ import org.springframework.core.Ordered;
|
|||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.http.server.reactive.HttpHandler;
|
||||
import org.springframework.validation.Validator;
|
||||
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
|
||||
import org.springframework.validation.beanvalidation.SpringValidatorAdapter;
|
||||
import org.springframework.web.reactive.HandlerMapping;
|
||||
import org.springframework.web.reactive.accept.CompositeContentTypeResolver;
|
||||
import org.springframework.web.reactive.config.WebFluxConfigurationSupport;
|
||||
import org.springframework.web.reactive.config.WebFluxConfigurer;
|
||||
import org.springframework.web.reactive.handler.SimpleUrlHandlerMapping;
|
||||
import org.springframework.web.reactive.resource.CachingResourceResolver;
|
||||
import org.springframework.web.reactive.resource.CachingResourceTransformer;
|
||||
|
@ -41,7 +51,6 @@ import org.springframework.web.reactive.result.method.annotation.RequestMappingH
|
|||
import org.springframework.web.reactive.result.method.annotation.RequestMappingHandlerMapping;
|
||||
import org.springframework.web.reactive.result.view.ViewResolutionResultHandler;
|
||||
import org.springframework.web.reactive.result.view.ViewResolver;
|
||||
import org.springframework.web.server.adapter.HttpWebHandlerAdapter;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
@ -152,6 +161,68 @@ public class WebFluxAnnotationAutoConfigurationTests {
|
|||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void validationNoJsr303ValidatorExposedByDefault() {
|
||||
load();
|
||||
assertThat(this.context.getBeansOfType(ValidatorFactory.class)).isEmpty();
|
||||
assertThat(this.context.getBeansOfType(javax.validation.Validator.class))
|
||||
.isEmpty();
|
||||
assertThat(this.context.getBeansOfType(Validator.class)).hasSize(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void validationCustomConfigurerTakesPrecedence() {
|
||||
load(WebFluxValidator.class);
|
||||
assertThat(this.context.getBeansOfType(ValidatorFactory.class)).isEmpty();
|
||||
assertThat(this.context.getBeansOfType(javax.validation.Validator.class))
|
||||
.isEmpty();
|
||||
assertThat(this.context.getBeansOfType(Validator.class)).hasSize(1);
|
||||
Validator validator = this.context.getBean(Validator.class);
|
||||
assertThat(validator)
|
||||
.isSameAs(this.context.getBean(WebFluxValidator.class).validator);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void validationCustomConfigurerTakesPrecedenceAndDoNotExposeJsr303() {
|
||||
load(WebFluxJsr303Validator.class);
|
||||
assertThat(this.context.getBeansOfType(ValidatorFactory.class)).isEmpty();
|
||||
assertThat(this.context.getBeansOfType(javax.validation.Validator.class))
|
||||
.isEmpty();
|
||||
assertThat(this.context.getBeansOfType(Validator.class)).hasSize(1);
|
||||
Validator validator = this.context.getBean(Validator.class);
|
||||
assertThat(validator).isInstanceOf(SpringValidator.class);
|
||||
assertThat(((SpringValidator) validator).getTarget())
|
||||
.isSameAs(this.context.getBean(WebFluxJsr303Validator.class).validator);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void validationJsr303CustomValidatorReusedAsSpringValidator() {
|
||||
load(CustomValidator.class);
|
||||
assertThat(this.context.getBeansOfType(ValidatorFactory.class)).hasSize(1);
|
||||
assertThat(this.context.getBeansOfType(javax.validation.Validator.class))
|
||||
.hasSize(1);
|
||||
assertThat(this.context.getBeansOfType(Validator.class)).hasSize(2);
|
||||
Validator validator = this.context.getBean("webFluxValidator", Validator.class);
|
||||
assertThat(validator).isInstanceOf(SpringValidator.class);
|
||||
assertThat(((SpringValidator) validator).getTarget())
|
||||
.isSameAs(this.context.getBean(javax.validation.Validator.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void validationJsr303ValidatorExposedAsSpringValidator() {
|
||||
load(Jsr303Validator.class);
|
||||
assertThat(this.context.getBeansOfType(ValidatorFactory.class)).isEmpty();
|
||||
assertThat(this.context.getBeansOfType(javax.validation.Validator.class))
|
||||
.hasSize(1);
|
||||
assertThat(this.context.getBeansOfType(Validator.class)).hasSize(1);
|
||||
Validator validator = this.context.getBean(Validator.class);
|
||||
assertThat(validator).isInstanceOf(SpringValidator.class);
|
||||
SpringValidatorAdapter target = ((SpringValidator) validator)
|
||||
.getTarget();
|
||||
assertThat(new DirectFieldAccessor(target).getPropertyValue("targetValidator"))
|
||||
.isSameAs(this.context.getBean(javax.validation.Validator.class));
|
||||
}
|
||||
|
||||
private void load(String... environment) {
|
||||
load(null, environment);
|
||||
}
|
||||
|
@ -216,4 +287,49 @@ public class WebFluxAnnotationAutoConfigurationTests {
|
|||
return (serverHttpRequest, serverHttpResponse) -> null;
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
protected static class WebFluxValidator implements WebFluxConfigurer {
|
||||
|
||||
private final Validator validator = mock(Validator.class);
|
||||
|
||||
@Override
|
||||
public Optional<Validator> getValidator() {
|
||||
return Optional.of(this.validator);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
protected static class WebFluxJsr303Validator implements WebFluxConfigurer {
|
||||
|
||||
private final LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
|
||||
|
||||
@Override
|
||||
public Optional<Validator> getValidator() {
|
||||
return Optional.of(this.validator);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class Jsr303Validator {
|
||||
|
||||
@Bean
|
||||
public javax.validation.Validator jsr303Validator() {
|
||||
return mock(javax.validation.Validator.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class CustomValidator {
|
||||
|
||||
@Bean
|
||||
public Validator customValidator() {
|
||||
return new LocalValidatorFactoryBean();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue