diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/AbstractEndpointHandlerMapping.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/AbstractEndpointHandlerMapping.java index 0f629cdb9c..45b16f2fd8 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/AbstractEndpointHandlerMapping.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/AbstractEndpointHandlerMapping.java @@ -29,7 +29,6 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.boot.actuate.endpoint.Endpoint; -import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration; import org.springframework.context.ApplicationContext; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; @@ -295,12 +294,13 @@ public abstract class AbstractEndpointHandlerMapping private static final class SkipPathExtensionContentNegotiation extends HandlerInterceptorAdapter { + private static final String SKIP_ATTRIBUTE = PathExtensionContentNegotiationStrategy.class + .getName() + ".SKIP"; + @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { - request.setAttribute( - WebMvcAutoConfiguration.SKIP_PATH_EXTENSION_CONTENT_NEGOTIATION_ATTRIBUTE, - Boolean.TRUE); + request.setAttribute(SKIP_ATTRIBUTE, Boolean.TRUE); return true; } diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/WebFluxAnnotationAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/WebFluxAnnotationAutoConfiguration.java index f690ccf66a..2bc60a3be4 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/WebFluxAnnotationAutoConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/WebFluxAnnotationAutoConfiguration.java @@ -24,17 +24,15 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.BeansException; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.BeanFactoryUtils; import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.ListableBeanFactory; import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.config.BeanDefinition; -import org.springframework.beans.factory.config.BeanFactoryPostProcessor; -import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.BeanDefinitionRegistry; -import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.AutoConfigureOrder; @@ -54,13 +52,13 @@ import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.ConfigurationCondition; import org.springframework.context.annotation.Import; -import org.springframework.context.annotation.Role; +import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; import org.springframework.core.Ordered; import org.springframework.core.annotation.AnnotationAwareOrderComparator; -import org.springframework.core.annotation.Order; import org.springframework.core.convert.converter.Converter; import org.springframework.core.convert.converter.GenericConverter; import org.springframework.core.type.AnnotatedTypeMetadata; +import org.springframework.core.type.AnnotationMetadata; import org.springframework.format.Formatter; import org.springframework.format.FormatterRegistry; import org.springframework.http.CacheControl; @@ -92,6 +90,7 @@ import org.springframework.web.reactive.result.view.ViewResolver; * @author Rob Winch * @author Stephane Nicoll * @author Andy Wilkinson + * @author Phillip Webb * @since 2.0.0 */ @Configuration @@ -102,15 +101,9 @@ import org.springframework.web.reactive.result.view.ViewResolver; @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10) public class WebFluxAnnotationAutoConfiguration { - @Bean - @Role(BeanDefinition.ROLE_INFRASTRUCTURE) - public static WebFluxValidatorPostProcessor mvcValidatorAliasPostProcessor() { - return new WebFluxValidatorPostProcessor(); - } - @Configuration @EnableConfigurationProperties({ ResourceProperties.class, WebFluxProperties.class }) - @Import(EnableWebFluxConfiguration.class) + @Import({ EnableWebFluxConfiguration.class, WebFluxValidatorRegistrar.class }) public static class WebFluxConfig implements WebFluxConfigurer { private static final Log logger = LogFactory.getLog(WebFluxConfig.class); @@ -306,8 +299,8 @@ public class WebFluxAnnotationAutoConfiguration { /** * Condition used to disable the default WebFlux validator registration. The - * {@link WebFluxValidatorPostProcessor} is used to configure the - * {@code webFluxValidator} bean. + * {@link WebFluxValidatorRegistrar} is used to register the {@code webFluxValidator} + * bean. */ static class DisableWebFluxValidatorCondition implements ConfigurationCondition { @@ -324,53 +317,55 @@ public class WebFluxAnnotationAutoConfiguration { } /** - * {@link BeanFactoryPostProcessor} to deal with the MVC validator bean registration. - * Applies the following rules: + * {@link ImportBeanDefinitionRegistrar} to deal with the WebFlux validator bean + * registration. Applies the following rules: * */ - @Order(Ordered.LOWEST_PRECEDENCE) - static class WebFluxValidatorPostProcessor - implements BeanDefinitionRegistryPostProcessor { + static class WebFluxValidatorRegistrar + implements ImportBeanDefinitionRegistrar, BeanFactoryAware { private static final String JSR303_VALIDATOR_CLASS = "javax.validation.Validator"; + private BeanFactory beanFactory; + @Override - public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) - throws BeansException { - if (registry instanceof ListableBeanFactory) { - postProcess(registry, (ListableBeanFactory) registry); - } + public void setBeanFactory(BeanFactory beanFactory) throws BeansException { + this.beanFactory = beanFactory; } @Override - public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) - throws BeansException { + public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, + BeanDefinitionRegistry registry) { + if (this.beanFactory instanceof ListableBeanFactory) { + registerOrAliasWebFluxValidator(registry, + (ListableBeanFactory) this.beanFactory); + } } - private void postProcess(BeanDefinitionRegistry registry, + private void registerOrAliasWebFluxValidator(BeanDefinitionRegistry registry, ListableBeanFactory beanFactory) { String[] validatorBeans = BeanFactoryUtils.beanNamesForTypeIncludingAncestors( beanFactory, Validator.class, false, false); if (validatorBeans.length == 0) { - registerMvcValidator(registry, beanFactory); + registerNewWebFluxValidator(registry, beanFactory); } else if (validatorBeans.length == 1) { registry.registerAlias(validatorBeans[0], "webFluxValidator"); } else { if (!ObjectUtils.containsElement(validatorBeans, "webFluxValidator")) { - registerMvcValidator(registry, beanFactory); + registerNewWebFluxValidator(registry, beanFactory); } } } - private void registerMvcValidator(BeanDefinitionRegistry registry, + private void registerNewWebFluxValidator(BeanDefinitionRegistry registry, ListableBeanFactory beanFactory) { RootBeanDefinition definition = new RootBeanDefinition(); definition.setBeanClass(getClass()); diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration.java index a9e0f49db6..83232c24dd 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration.java @@ -32,6 +32,7 @@ import org.apache.commons.logging.LogFactory; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.BeanFactoryUtils; import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.InitializingBean; @@ -39,11 +40,7 @@ import org.springframework.beans.factory.ListableBeanFactory; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.config.BeanDefinition; -import org.springframework.beans.factory.config.BeanFactoryPostProcessor; -import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.BeanDefinitionRegistry; -import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.AutoConfigureOrder; @@ -72,14 +69,14 @@ import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.ConfigurationCondition; import org.springframework.context.annotation.Import; +import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; import org.springframework.context.annotation.Primary; -import org.springframework.context.annotation.Role; import org.springframework.core.Ordered; -import org.springframework.core.annotation.Order; import org.springframework.core.convert.converter.Converter; import org.springframework.core.convert.converter.GenericConverter; import org.springframework.core.io.Resource; import org.springframework.core.type.AnnotatedTypeMetadata; +import org.springframework.core.type.AnnotationMetadata; import org.springframework.format.Formatter; import org.springframework.format.FormatterRegistry; import org.springframework.format.datetime.DateFormatter; @@ -160,19 +157,6 @@ public class WebMvcAutoConfiguration { public static final String DEFAULT_SUFFIX = ""; - /** - * Attribute that can be added to the web request when the - * {@link PathExtensionContentNegotiationStrategy} should be be skipped. - */ - public static final String SKIP_PATH_EXTENSION_CONTENT_NEGOTIATION_ATTRIBUTE = PathExtensionContentNegotiationStrategy.class - .getName() + ".SKIP"; - - @Bean - @Role(BeanDefinition.ROLE_INFRASTRUCTURE) - public static MvcValidatorPostProcessor mvcValidatorAliasPostProcessor() { - return new MvcValidatorPostProcessor(); - } - @Bean @ConditionalOnMissingBean(HiddenHttpMethodFilter.class) public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter() { @@ -189,7 +173,7 @@ public class WebMvcAutoConfiguration { // Defined as a nested config to ensure WebMvcConfigurerAdapter is not read when not // on the classpath @Configuration - @Import(EnableWebMvcConfiguration.class) + @Import({ EnableWebMvcConfiguration.class, MvcValidatorRegistrar.class }) @EnableConfigurationProperties({ WebMvcProperties.class, ResourceProperties.class }) public static class WebMvcAutoConfigurationAdapter extends WebMvcConfigurerAdapter { @@ -623,6 +607,9 @@ public class WebMvcAutoConfiguration { static class OptionalPathExtensionContentNegotiationStrategy implements ContentNegotiationStrategy { + private static final String SKIP_ATTRIBUTE = PathExtensionContentNegotiationStrategy.class + .getName() + ".SKIP"; + private final ContentNegotiationStrategy delegate; OptionalPathExtensionContentNegotiationStrategy( @@ -633,8 +620,7 @@ public class WebMvcAutoConfiguration { @Override public List resolveMediaTypes(NativeWebRequest webRequest) throws HttpMediaTypeNotAcceptableException { - Object skip = webRequest.getAttribute( - SKIP_PATH_EXTENSION_CONTENT_NEGOTIATION_ATTRIBUTE, + Object skip = webRequest.getAttribute(SKIP_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST); if (skip != null && Boolean.parseBoolean(skip.toString())) { return Collections.emptyList(); @@ -646,7 +632,7 @@ public class WebMvcAutoConfiguration { /** * Condition used to disable the default MVC validator registration. The - * {@link MvcValidatorPostProcessor} is used to configure the {@code mvcValidator} + * {@link MvcValidatorRegistrar} is actually used to register the {@code mvcValidator} * bean. */ static class DisableMvcValidatorCondition implements ConfigurationCondition { @@ -664,8 +650,8 @@ public class WebMvcAutoConfiguration { } /** - * {@link BeanFactoryPostProcessor} to deal with the MVC validator bean registration. - * Applies the following rules: + * {@link ImportBeanDefinitionRegistrar} to deal with the MVC validator bean + * registration. Applies the following rules: * */ - @Order(Ordered.LOWEST_PRECEDENCE) - static class MvcValidatorPostProcessor - implements BeanDefinitionRegistryPostProcessor { + static class MvcValidatorRegistrar + implements ImportBeanDefinitionRegistrar, BeanFactoryAware { private static final String JSR303_VALIDATOR_CLASS = "javax.validation.Validator"; + private BeanFactory beanFactory; + @Override - public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) - throws BeansException { - if (registry instanceof ListableBeanFactory) { - postProcess(registry, (ListableBeanFactory) registry); - } + public void setBeanFactory(BeanFactory beanFactory) throws BeansException { + this.beanFactory = beanFactory; } @Override - public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) - throws BeansException { + public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, + BeanDefinitionRegistry registry) { + if (this.beanFactory instanceof ListableBeanFactory) { + registerOrAliasMvcValidator(registry, + (ListableBeanFactory) this.beanFactory); + } } - private void postProcess(BeanDefinitionRegistry registry, + private void registerOrAliasMvcValidator(BeanDefinitionRegistry registry, ListableBeanFactory beanFactory) { String[] validatorBeans = BeanFactoryUtils.beanNamesForTypeIncludingAncestors( beanFactory, Validator.class, false, false); if (validatorBeans.length == 0) { - registerMvcValidator(registry, beanFactory); + registerNewMvcValidator(registry, beanFactory); } else if (validatorBeans.length == 1) { registry.registerAlias(validatorBeans[0], "mvcValidator"); } else { if (!ObjectUtils.containsElement(validatorBeans, "mvcValidator")) { - registerMvcValidator(registry, beanFactory); + registerNewMvcValidator(registry, beanFactory); } } } - private void registerMvcValidator(BeanDefinitionRegistry registry, + private void registerNewMvcValidator(BeanDefinitionRegistry registry, ListableBeanFactory beanFactory) { RootBeanDefinition definition = new RootBeanDefinition(); definition.setBeanClass(getClass()); diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/reactive/WebFluxAnnotationAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/reactive/WebFluxAnnotationAutoConfigurationTests.java index 91dbee202b..001699cfa9 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/reactive/WebFluxAnnotationAutoConfigurationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/reactive/WebFluxAnnotationAutoConfigurationTests.java @@ -258,7 +258,7 @@ public class WebFluxAnnotationAutoConfigurationTests { } @Test - public void validatorWhenExcludingValidatorAutoConfigurationShouldUseMvc() + public void validatorWhenExcludingValidatorAutoConfigurationShouldUseWebFlux() throws Exception { load(null, new Class[] { ValidationAutoConfiguration.class }); Object webFluxValidator = this.context.getBean("webFluxValidator"); @@ -271,7 +271,7 @@ public class WebFluxAnnotationAutoConfigurationTests { } @Test - public void validatorWhenMultipleValidatorsAndNoWebFluxValidatorShouldAddMvc() + public void validatorWhenMultipleValidatorsAndNoWebFluxValidatorShouldAddWebFlux() throws Exception { load(MultipleValidatorsAndNoWebFluxValidator.class); Object customValidator1 = this.context.getBean("customValidator1"); @@ -288,7 +288,7 @@ public class WebFluxAnnotationAutoConfigurationTests { } @Test - public void validatorWhenMultipleValidatorsAndWebFluxValidatorShouldUseMvc() + public void validatorWhenMultipleValidatorsAndWebFluxValidatorShouldUseWebFlux() throws Exception { load(MultipleValidatorsAndWebFluxValidator.class); Object customValidator = this.context.getBean("customValidator");