From da4624a8820635e56e4464a4ac282c78d16194a4 Mon Sep 17 00:00:00 2001 From: Brian Clozel Date: Thu, 23 Aug 2018 15:35:34 +0200 Subject: [PATCH] Consider only current context in WebFlux setup This commit introduces a subclass of `DispatcherHandler` that only considers the current context when looking for WebFlux infrastructure beans. This avoids issues where a child context (e.g. with Actuator) picks up infrastructure beans from the parent context and exposes all endpoints instead of getting only the ones from the current context. Closes gh-14012 --- .../SingleContextDispatcherHandler.java | 56 +++++++++++++++++++ .../reactive/WebFluxAutoConfiguration.java | 6 ++ .../WebFluxAutoConfigurationTests.java | 33 +++++++++++ 3 files changed, 95 insertions(+) create mode 100644 spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/SingleContextDispatcherHandler.java diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/SingleContextDispatcherHandler.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/SingleContextDispatcherHandler.java new file mode 100644 index 0000000000..a6e57e4b0e --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/SingleContextDispatcherHandler.java @@ -0,0 +1,56 @@ +/* + * Copyright 2012-2018 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.reactive; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Map; + +import org.springframework.context.ApplicationContext; +import org.springframework.core.annotation.AnnotationAwareOrderComparator; +import org.springframework.web.reactive.DispatcherHandler; +import org.springframework.web.reactive.HandlerAdapter; +import org.springframework.web.reactive.HandlerMapping; +import org.springframework.web.reactive.HandlerResultHandler; + +/** + * {@link DispatcherHandler} implementation that only checks for infrastructure beans in + * the current application context (i.e. does not consider the parent context). + * + * @author Brian Clozel + * @since 2.1.0 + */ +public class SingleContextDispatcherHandler extends DispatcherHandler { + + @Override + protected void initStrategies(ApplicationContext context) { + Map mappingBeans = context + .getBeansOfType(HandlerMapping.class, true, false); + ArrayList mappings = new ArrayList<>(mappingBeans.values()); + AnnotationAwareOrderComparator.sort(mappings); + this.handlerMappings = Collections.unmodifiableList(mappings); + Map adapterBeans = context + .getBeansOfType(HandlerAdapter.class, true, false); + this.handlerAdapters = new ArrayList(adapterBeans.values()); + AnnotationAwareOrderComparator.sort(this.handlerAdapters); + Map beans = context + .getBeansOfType(HandlerResultHandler.class, true, false); + this.resultHandlers = new ArrayList<>(beans.values()); + AnnotationAwareOrderComparator.sort(this.resultHandlers); + } + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/WebFluxAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/WebFluxAutoConfiguration.java index b37c11d3dc..6a31d78adb 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/WebFluxAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/WebFluxAutoConfiguration.java @@ -57,6 +57,7 @@ import org.springframework.http.codec.ServerCodecConfigurer; import org.springframework.util.ClassUtils; import org.springframework.validation.Validator; import org.springframework.web.filter.reactive.HiddenHttpMethodFilter; +import org.springframework.web.reactive.DispatcherHandler; import org.springframework.web.reactive.config.DelegatingWebFluxConfiguration; import org.springframework.web.reactive.config.EnableWebFlux; import org.springframework.web.reactive.config.ResourceChainRegistration; @@ -245,6 +246,11 @@ public class WebFluxAutoConfiguration { return conversionService; } + @Override + public DispatcherHandler webHandler() { + return new SingleContextDispatcherHandler(); + } + @Bean @Override public Validator webFluxValidator() { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/reactive/WebFluxAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/reactive/WebFluxAutoConfigurationTests.java index 74cc41e867..650f4ceae8 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/reactive/WebFluxAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/reactive/WebFluxAutoConfigurationTests.java @@ -28,6 +28,8 @@ import org.springframework.beans.DirectFieldAccessor; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration; import org.springframework.boot.autoconfigure.validation.ValidatorAdapter; +import org.springframework.boot.test.context.assertj.ApplicationContextAssert; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner; import org.springframework.boot.web.codec.CodecCustomizer; import org.springframework.boot.web.reactive.filter.OrderedHiddenHttpMethodFilter; @@ -44,6 +46,7 @@ import org.springframework.test.util.ReflectionTestUtils; import org.springframework.validation.Validator; import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; import org.springframework.web.filter.reactive.HiddenHttpMethodFilter; +import org.springframework.web.reactive.DispatcherHandler; import org.springframework.web.reactive.HandlerMapping; import org.springframework.web.reactive.accept.RequestedContentTypeResolver; import org.springframework.web.reactive.config.WebFluxConfigurationSupport; @@ -398,6 +401,26 @@ public class WebFluxAutoConfigurationTests { }); } + @Test + public void shouldNotGetHandlerMappingsFromParentContext() { + ApplicationContextRunner parentRunner = new ApplicationContextRunner() + .withUserConfiguration(CustomHandlerMapping.class); + parentRunner.run((parent) -> { + this.contextRunner.withParent(parent).run((context) -> { + assertThat(parent).hasSingleBean(HandlerMapping.class); + assertThat(context).getBeans(HandlerMapping.class).hasSize(4); + assertThat(context) + .getBeans(HandlerMapping.class, + ApplicationContextAssert.Scope.NO_ANCESTORS) + .doesNotContainKey("customHandlerMapping"); + assertThat(context).hasSingleBean(DispatcherHandler.class); + DispatcherHandler dispatcherHandler = context + .getBean(DispatcherHandler.class); + assertThat(dispatcherHandler.getHandlerMappings()).hasSize(3); + }); + }); + } + @Configuration protected static class CustomArgumentResolvers { @@ -564,4 +587,14 @@ public class WebFluxAutoConfigurationTests { } + @Configuration + static class CustomHandlerMapping { + + @Bean + public HandlerMapping customHandlerMapping() { + return mock(HandlerMapping.class); + } + + } + }