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 0c0c808fd50..b37c11d3dcc 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 @@ -71,6 +71,8 @@ import org.springframework.web.reactive.resource.ResourceResolver; import org.springframework.web.reactive.resource.VersionResourceResolver; import org.springframework.web.reactive.result.method.HandlerMethodArgumentResolver; import org.springframework.web.reactive.result.method.annotation.ArgumentResolverConfigurer; +import org.springframework.web.reactive.result.method.annotation.RequestMappingHandlerAdapter; +import org.springframework.web.reactive.result.method.annotation.RequestMappingHandlerMapping; import org.springframework.web.reactive.result.view.ViewResolver; /** @@ -226,8 +228,12 @@ public class WebFluxAutoConfiguration { private final WebFluxProperties webFluxProperties; - public EnableWebFluxConfiguration(WebFluxProperties webFluxProperties) { + private final WebFluxRegistrations webFluxRegistrations; + + public EnableWebFluxConfiguration(WebFluxProperties webFluxProperties, + ObjectProvider webFluxRegistrations) { this.webFluxProperties = webFluxProperties; + this.webFluxRegistrations = webFluxRegistrations.getIfUnique(); } @Bean @@ -249,6 +255,24 @@ public class WebFluxAutoConfiguration { return ValidatorAdapter.get(getApplicationContext(), getValidator()); } + @Override + protected RequestMappingHandlerAdapter createRequestMappingHandlerAdapter() { + if (this.webFluxRegistrations != null && this.webFluxRegistrations + .getRequestMappingHandlerAdapter() != null) { + return this.webFluxRegistrations.getRequestMappingHandlerAdapter(); + } + return super.createRequestMappingHandlerAdapter(); + } + + @Override + protected RequestMappingHandlerMapping createRequestMappingHandlerMapping() { + if (this.webFluxRegistrations != null && this.webFluxRegistrations + .getRequestMappingHandlerMapping() != null) { + return this.webFluxRegistrations.getRequestMappingHandlerMapping(); + } + return super.createRequestMappingHandlerMapping(); + } + } @Configuration diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/WebFluxRegistrations.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/WebFluxRegistrations.java new file mode 100644 index 00000000000..fa57a8a2d25 --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/WebFluxRegistrations.java @@ -0,0 +1,53 @@ +/* + * 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 org.springframework.web.reactive.result.method.annotation.RequestMappingHandlerAdapter; +import org.springframework.web.reactive.result.method.annotation.RequestMappingHandlerMapping; + +/** + * Interface to register key components of the {@link WebFluxAutoConfiguration} in place + * of the default ones provided by Spring WebFlux. + *

+ * All custom instances are later processed by Boot and Spring WebFlux configurations. A + * single instance of this component should be registered, otherwise making it impossible + * to choose from redundant WebFlux components. + * + * @author Artsiom Yudovin + * @since 2.1.0 + * @see org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration.EnableWebFluxConfiguration + */ +public interface WebFluxRegistrations { + + /** + * Return the custom {@link RequestMappingHandlerMapping} that should be used and + * processed by the WebFlux configuration. + * @return the custom {@link RequestMappingHandlerMapping} instance + */ + default RequestMappingHandlerMapping getRequestMappingHandlerMapping() { + return null; + } + + /** + * Return the custom {@link RequestMappingHandlerAdapter} that should be used and + * processed by the WebFlux configuration. + * @return the custom {@link RequestMappingHandlerAdapter} instance + */ + default RequestMappingHandlerAdapter getRequestMappingHandlerAdapter() { + return null; + } + +} 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 112ae3b7ced..74cc41e867b 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 @@ -33,6 +33,7 @@ import org.springframework.boot.web.codec.CodecCustomizer; import org.springframework.boot.web.reactive.filter.OrderedHiddenHttpMethodFilter; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; import org.springframework.core.io.ClassPathResource; @@ -370,6 +371,33 @@ public class WebFluxAutoConfigurationTests { }); } + @Test + public void customRequestMappingHandlerMapping() { + this.contextRunner.withUserConfiguration(CustomRequestMappingHandlerMapping.class) + .run((context) -> assertThat(context) + .getBean(RequestMappingHandlerMapping.class) + .isInstanceOf(MyRequestMappingHandlerMapping.class)); + } + + @Test + public void customRequestMappingHandlerAdapter() { + this.contextRunner.withUserConfiguration(CustomRequestMappingHandlerAdapter.class) + .run((context) -> assertThat(context) + .getBean(RequestMappingHandlerAdapter.class) + .isInstanceOf(MyRequestMappingHandlerAdapter.class)); + } + + @Test + public void multipleWebFluxRegistrations() { + this.contextRunner.withUserConfiguration(MultipleWebFluxRegistrations.class) + .run((context) -> { + assertThat(context.getBean(RequestMappingHandlerMapping.class)) + .isNotInstanceOf(MyRequestMappingHandlerMapping.class); + assertThat(context.getBean(RequestMappingHandlerAdapter.class)) + .isNotInstanceOf(MyRequestMappingHandlerAdapter.class); + }); + } + @Configuration protected static class CustomArgumentResolvers { @@ -485,4 +513,55 @@ public class WebFluxAutoConfigurationTests { } + @Configuration + static class CustomRequestMappingHandlerAdapter { + + @Bean + public WebFluxRegistrations webMvcRegistrationsHandlerAdapter() { + return new WebFluxRegistrations() { + + @Override + public RequestMappingHandlerAdapter getRequestMappingHandlerAdapter() { + return new WebFluxAutoConfigurationTests.MyRequestMappingHandlerAdapter(); + } + + }; + } + + } + + private static class MyRequestMappingHandlerAdapter + extends RequestMappingHandlerAdapter { + + } + + @Configuration + @Import({ WebFluxAutoConfigurationTests.CustomRequestMappingHandlerMapping.class, + WebFluxAutoConfigurationTests.CustomRequestMappingHandlerAdapter.class }) + static class MultipleWebFluxRegistrations { + + } + + @Configuration + static class CustomRequestMappingHandlerMapping { + + @Bean + public WebFluxRegistrations webMvcRegistrationsHandlerMapping() { + return new WebFluxRegistrations() { + + @Override + public RequestMappingHandlerMapping getRequestMappingHandlerMapping() { + return new MyRequestMappingHandlerMapping(); + } + + }; + } + + } + + private static class MyRequestMappingHandlerMapping + extends RequestMappingHandlerMapping { + + } + }