diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration.java index 0812853e7a7..7953a7e2384 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration.java @@ -56,6 +56,7 @@ import org.springframework.boot.autoconfigure.web.format.WebConversionService; import org.springframework.boot.autoconfigure.web.servlet.WebMvcProperties.Format; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.convert.ApplicationConversionService; +import org.springframework.boot.web.servlet.ServletRegistrationBean; import org.springframework.boot.web.servlet.filter.OrderedFormContentFilter; import org.springframework.boot.web.servlet.filter.OrderedHiddenHttpMethodFilter; import org.springframework.boot.web.servlet.filter.OrderedRequestContextFilter; @@ -191,18 +192,22 @@ public class WebMvcAutoConfiguration { private final ObjectProvider dispatcherServletPath; + private final ObjectProvider> servletRegistrations; + final ResourceHandlerRegistrationCustomizer resourceHandlerRegistrationCustomizer; public WebMvcAutoConfigurationAdapter(ResourceProperties resourceProperties, WebMvcProperties mvcProperties, ListableBeanFactory beanFactory, ObjectProvider messageConvertersProvider, ObjectProvider resourceHandlerRegistrationCustomizerProvider, - ObjectProvider dispatcherServletPath) { + ObjectProvider dispatcherServletPath, + ObjectProvider> servletRegistrations) { this.resourceProperties = resourceProperties; this.mvcProperties = mvcProperties; this.beanFactory = beanFactory; this.messageConvertersProvider = messageConvertersProvider; this.resourceHandlerRegistrationCustomizer = resourceHandlerRegistrationCustomizerProvider.getIfAvailable(); this.dispatcherServletPath = dispatcherServletPath; + this.servletRegistrations = servletRegistrations; this.mvcProperties.checkConfiguration(); } @@ -239,7 +244,7 @@ public class WebMvcAutoConfiguration { this.mvcProperties.getPathmatch().isUseRegisteredSuffixPattern()); this.dispatcherServletPath.ifAvailable((dispatcherPath) -> { String servletUrlMapping = dispatcherPath.getServletUrlMapping(); - if (servletUrlMapping.equals("/")) { + if (servletUrlMapping.equals("/") && singleDispatcherServlet()) { UrlPathHelper urlPathHelper = new UrlPathHelper(); urlPathHelper.setAlwaysUseFullPath(true); configurer.setUrlPathHelper(urlPathHelper); @@ -247,6 +252,11 @@ public class WebMvcAutoConfiguration { }); } + private boolean singleDispatcherServlet() { + return this.servletRegistrations.stream().map(ServletRegistrationBean::getServlet) + .filter(DispatcherServlet.class::isInstance).count() == 1; + } + @Override @SuppressWarnings("deprecation") public void configureContentNegotiation(ContentNegotiationConfigurer configurer) { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfigurationTests.java index b87b03ab49b..fa03544f5e1 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfigurationTests.java @@ -53,6 +53,7 @@ import org.springframework.boot.test.context.runner.ContextConsumer; import org.springframework.boot.test.context.runner.WebApplicationContextRunner; import org.springframework.boot.web.server.WebServerFactoryCustomizerBeanPostProcessor; import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.boot.web.servlet.ServletRegistrationBean; import org.springframework.boot.web.servlet.filter.OrderedFormContentFilter; import org.springframework.boot.web.servlet.server.ServletWebServerFactory; import org.springframework.context.ApplicationContext; @@ -84,6 +85,7 @@ import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import org.springframework.web.filter.FormContentFilter; import org.springframework.web.filter.HiddenHttpMethodFilter; import org.springframework.web.filter.RequestContextFilter; +import org.springframework.web.servlet.DispatcherServlet; import org.springframework.web.servlet.HandlerAdapter; import org.springframework.web.servlet.HandlerExceptionResolver; import org.springframework.web.servlet.HandlerMapping; @@ -893,6 +895,23 @@ class WebMvcAutoConfigurationTests { }); } + @Test + void urlPathHelperDoesNotUseFullPathWithAdditionalDispatcherServlet() { + this.contextRunner.withUserConfiguration(AdditionalDispatcherServletConfiguration.class).run((context) -> { + UrlPathHelper urlPathHelper = context.getBean(UrlPathHelper.class); + assertThat(urlPathHelper).extracting("alwaysUseFullPath").isEqualTo(false); + }); + } + + @Test + void urlPathHelperDoesNotUseFullPathWithAdditionalUntypedDispatcherServlet() { + this.contextRunner.withUserConfiguration(AdditionalUntypedDispatcherServletConfiguration.class) + .run((context) -> { + UrlPathHelper urlPathHelper = context.getBean(UrlPathHelper.class); + assertThat(urlPathHelper).extracting("alwaysUseFullPath").isEqualTo(false); + }); + } + private void assertCacheControl(AssertableWebApplicationContext context) { Map handlerMap = getHandlerMap(context.getBean("resourceHandlerMapping", HandlerMapping.class)); assertThat(handlerMap).hasSize(2); @@ -1276,4 +1295,24 @@ class WebMvcAutoConfigurationTests { } + @Configuration(proxyBeanMethods = false) + static class AdditionalDispatcherServletConfiguration { + + @Bean + ServletRegistrationBean additionalDispatcherServlet() { + return new ServletRegistrationBean<>(new DispatcherServlet()); + } + + } + + @Configuration(proxyBeanMethods = false) + static class AdditionalUntypedDispatcherServletConfiguration { + + @Bean + ServletRegistrationBean additionalDispatcherServlet() { + return new ServletRegistrationBean<>(new DispatcherServlet()); + } + + } + }