Split up DispatcherServletConfiguration condition
Prior to this commit, defining a custom `DispatcherServlet` and/or a `ServletRegistrationBean` with the default name would turn off completely the `DispatcherServletAutoConfiguration`. This commit splits this auto-configuration in two parts: - First, a `DispatcherServlet` is automatically registered if no instance is already defined with the default name. - Then, a `ServletRegistrationBean` is registered is registered if a `DispatcherServlet` instance exists with the default name *and* no `ServletRegistrationBean` exists with the default name This allows developers to register manually a `ServletRegistrationBean` or a `DispatcherServlet` without having to redefine the whole auto-configuration. Fixes gh-4893 Closes gh-6108
This commit is contained in:
committed by
Phillip Webb
parent
d87287fe72
commit
091664078f
@@ -40,6 +40,7 @@ import org.springframework.context.annotation.Bean;
|
|||||||
import org.springframework.context.annotation.ConditionContext;
|
import org.springframework.context.annotation.ConditionContext;
|
||||||
import org.springframework.context.annotation.Conditional;
|
import org.springframework.context.annotation.Conditional;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.Import;
|
||||||
import org.springframework.core.Ordered;
|
import org.springframework.core.Ordered;
|
||||||
import org.springframework.core.annotation.Order;
|
import org.springframework.core.annotation.Order;
|
||||||
import org.springframework.core.type.AnnotatedTypeMetadata;
|
import org.springframework.core.type.AnnotatedTypeMetadata;
|
||||||
@@ -55,6 +56,7 @@ import org.springframework.web.servlet.DispatcherServlet;
|
|||||||
* @author Phillip Webb
|
* @author Phillip Webb
|
||||||
* @author Dave Syer
|
* @author Dave Syer
|
||||||
* @author Stephane Nicoll
|
* @author Stephane Nicoll
|
||||||
|
* @author Brian Clozel
|
||||||
*/
|
*/
|
||||||
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
|
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
|
||||||
@Configuration
|
@Configuration
|
||||||
@@ -79,18 +81,10 @@ public class DispatcherServletAutoConfiguration {
|
|||||||
@EnableConfigurationProperties(WebMvcProperties.class)
|
@EnableConfigurationProperties(WebMvcProperties.class)
|
||||||
protected static class DispatcherServletConfiguration {
|
protected static class DispatcherServletConfiguration {
|
||||||
|
|
||||||
private final ServerProperties server;
|
|
||||||
|
|
||||||
private final WebMvcProperties webMvcProperties;
|
private final WebMvcProperties webMvcProperties;
|
||||||
|
|
||||||
private final MultipartConfigElement multipartConfig;
|
public DispatcherServletConfiguration(WebMvcProperties webMvcProperties) {
|
||||||
|
|
||||||
public DispatcherServletConfiguration(ServerProperties server,
|
|
||||||
WebMvcProperties webMvcProperties,
|
|
||||||
ObjectProvider<MultipartConfigElement> multipartConfigProvider) {
|
|
||||||
this.server = server;
|
|
||||||
this.webMvcProperties = webMvcProperties;
|
this.webMvcProperties = webMvcProperties;
|
||||||
this.multipartConfig = multipartConfigProvider.getIfAvailable();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
|
@Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
|
||||||
@@ -105,10 +99,43 @@ public class DispatcherServletAutoConfiguration {
|
|||||||
return dispatcherServlet;
|
return dispatcherServlet;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@ConditionalOnBean(MultipartResolver.class)
|
||||||
|
@ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME)
|
||||||
|
public MultipartResolver multipartResolver(MultipartResolver resolver) {
|
||||||
|
// Detect if the user has created a MultipartResolver but named it incorrectly
|
||||||
|
return resolver;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@Conditional(DispatcherServletRegistrationCondition.class)
|
||||||
|
@ConditionalOnClass(ServletRegistration.class)
|
||||||
|
@EnableConfigurationProperties(WebMvcProperties.class)
|
||||||
|
@Import(DispatcherServletConfiguration.class)
|
||||||
|
protected static class DispatcherServletRegistrationConfiguration {
|
||||||
|
|
||||||
|
private final ServerProperties serverProperties;
|
||||||
|
|
||||||
|
private final WebMvcProperties webMvcProperties;
|
||||||
|
|
||||||
|
private final MultipartConfigElement multipartConfig;
|
||||||
|
|
||||||
|
public DispatcherServletRegistrationConfiguration(
|
||||||
|
ServerProperties serverProperties, WebMvcProperties webMvcProperties,
|
||||||
|
ObjectProvider<MultipartConfigElement> multipartConfigProvider) {
|
||||||
|
this.serverProperties = serverProperties;
|
||||||
|
this.webMvcProperties = webMvcProperties;
|
||||||
|
this.multipartConfig = multipartConfigProvider.getIfAvailable();
|
||||||
|
}
|
||||||
|
|
||||||
@Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
|
@Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
|
||||||
public ServletRegistrationBean dispatcherServletRegistration() {
|
@ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
|
||||||
|
public ServletRegistrationBean dispatcherServletRegistration(
|
||||||
|
DispatcherServlet dispatcherServlet) {
|
||||||
ServletRegistrationBean registration = new ServletRegistrationBean(
|
ServletRegistrationBean registration = new ServletRegistrationBean(
|
||||||
dispatcherServlet(), this.server.getServletMapping());
|
dispatcherServlet, this.serverProperties.getServletMapping());
|
||||||
registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
|
registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
|
||||||
registration.setLoadOnStartup(
|
registration.setLoadOnStartup(
|
||||||
this.webMvcProperties.getServlet().getLoadOnStartup());
|
this.webMvcProperties.getServlet().getLoadOnStartup());
|
||||||
@@ -118,14 +145,6 @@ public class DispatcherServletAutoConfiguration {
|
|||||||
return registration;
|
return registration;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
|
||||||
@ConditionalOnBean(MultipartResolver.class)
|
|
||||||
@ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME)
|
|
||||||
public MultipartResolver multipartResolver(MultipartResolver resolver) {
|
|
||||||
// Detect if the user has created a MultipartResolver but named it incorrectly
|
|
||||||
return resolver;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Order(Ordered.LOWEST_PRECEDENCE - 10)
|
@Order(Ordered.LOWEST_PRECEDENCE - 10)
|
||||||
@@ -135,40 +154,56 @@ public class DispatcherServletAutoConfiguration {
|
|||||||
public ConditionOutcome getMatchOutcome(ConditionContext context,
|
public ConditionOutcome getMatchOutcome(ConditionContext context,
|
||||||
AnnotatedTypeMetadata metadata) {
|
AnnotatedTypeMetadata metadata) {
|
||||||
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
|
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
|
||||||
ConditionOutcome outcome = checkServlets(beanFactory);
|
List<String> dispatchServletBeans = Arrays.asList(beanFactory
|
||||||
|
.getBeanNamesForType(DispatcherServlet.class, false, false));
|
||||||
|
if (dispatchServletBeans.contains(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)) {
|
||||||
|
return ConditionOutcome.noMatch("found DispatcherServlet named "
|
||||||
|
+ DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
|
||||||
|
}
|
||||||
|
if (beanFactory.containsBean(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)) {
|
||||||
|
return ConditionOutcome.noMatch("found non-DispatcherServlet named "
|
||||||
|
+ DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
|
||||||
|
}
|
||||||
|
if (dispatchServletBeans.isEmpty()) {
|
||||||
|
return ConditionOutcome.match("no DispatcherServlet found");
|
||||||
|
}
|
||||||
|
return ConditionOutcome
|
||||||
|
.match("one or more DispatcherServlets found and none is named "
|
||||||
|
+ DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Order(Ordered.LOWEST_PRECEDENCE - 10)
|
||||||
|
private static class DispatcherServletRegistrationCondition
|
||||||
|
extends SpringBootCondition {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConditionOutcome getMatchOutcome(ConditionContext context,
|
||||||
|
AnnotatedTypeMetadata metadata) {
|
||||||
|
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
|
||||||
|
ConditionOutcome outcome = checkDefaultDispatcherName(beanFactory);
|
||||||
if (!outcome.isMatch()) {
|
if (!outcome.isMatch()) {
|
||||||
return outcome;
|
return outcome;
|
||||||
}
|
}
|
||||||
return checkServletRegistrations(beanFactory);
|
return checkServletRegistration(beanFactory);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ConditionOutcome checkServlets(
|
private ConditionOutcome checkDefaultDispatcherName(
|
||||||
ConfigurableListableBeanFactory beanFactory) {
|
ConfigurableListableBeanFactory beanFactory) {
|
||||||
List<String> servlets = Arrays.asList(beanFactory
|
List<String> servlets = Arrays.asList(beanFactory
|
||||||
.getBeanNamesForType(DispatcherServlet.class, false, false));
|
.getBeanNamesForType(DispatcherServlet.class, false, false));
|
||||||
boolean containsDispatcherBean = beanFactory
|
boolean containsDispatcherBean = beanFactory
|
||||||
.containsBean(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
|
.containsBean(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
|
||||||
if (servlets.isEmpty()) {
|
if (containsDispatcherBean
|
||||||
if (containsDispatcherBean) {
|
&& !servlets.contains(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)) {
|
||||||
return ConditionOutcome.noMatch("found no DispatcherServlet "
|
|
||||||
+ "but a non-DispatcherServlet named "
|
|
||||||
+ DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
|
|
||||||
}
|
|
||||||
return ConditionOutcome.match("no DispatcherServlet found");
|
|
||||||
}
|
|
||||||
if (servlets.contains(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)) {
|
|
||||||
return ConditionOutcome.noMatch("found DispatcherServlet named "
|
|
||||||
+ DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
|
|
||||||
}
|
|
||||||
if (containsDispatcherBean) {
|
|
||||||
return ConditionOutcome.noMatch("found non-DispatcherServlet named "
|
return ConditionOutcome.noMatch("found non-DispatcherServlet named "
|
||||||
+ DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
|
+ DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
|
||||||
}
|
}
|
||||||
return ConditionOutcome.match("one or more DispatcherServlets "
|
return ConditionOutcome.match();
|
||||||
+ "found and none is named " + DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private ConditionOutcome checkServletRegistrations(
|
private ConditionOutcome checkServletRegistration(
|
||||||
ConfigurableListableBeanFactory beanFactory) {
|
ConfigurableListableBeanFactory beanFactory) {
|
||||||
List<String> registrations = Arrays.asList(beanFactory
|
List<String> registrations = Arrays.asList(beanFactory
|
||||||
.getBeanNamesForType(ServletRegistrationBean.class, false, false));
|
.getBeanNamesForType(ServletRegistrationBean.class, false, false));
|
||||||
@@ -194,7 +229,6 @@ public class DispatcherServletAutoConfiguration {
|
|||||||
return ConditionOutcome
|
return ConditionOutcome
|
||||||
.match("one or more ServletRegistrationBeans is found and none is named "
|
.match("one or more ServletRegistrationBeans is found and none is named "
|
||||||
+ DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME);
|
+ DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ import org.junit.After;
|
|||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import org.springframework.beans.DirectFieldAccessor;
|
import org.springframework.beans.DirectFieldAccessor;
|
||||||
import org.springframework.beans.factory.UnsatisfiedDependencyException;
|
|
||||||
import org.springframework.boot.test.util.EnvironmentTestUtils;
|
import org.springframework.boot.test.util.EnvironmentTestUtils;
|
||||||
import org.springframework.boot.web.servlet.MultipartConfigFactory;
|
import org.springframework.boot.web.servlet.MultipartConfigFactory;
|
||||||
import org.springframework.boot.web.servlet.ServletRegistrationBean;
|
import org.springframework.boot.web.servlet.ServletRegistrationBean;
|
||||||
@@ -44,6 +43,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
|||||||
*
|
*
|
||||||
* @author Dave Syer
|
* @author Dave Syer
|
||||||
* @author Andy Wilkinson
|
* @author Andy Wilkinson
|
||||||
|
* @author Brian Clozel
|
||||||
*/
|
*/
|
||||||
public class DispatcherServletAutoConfigurationTests {
|
public class DispatcherServletAutoConfigurationTests {
|
||||||
|
|
||||||
@@ -70,24 +70,38 @@ public class DispatcherServletAutoConfigurationTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void registrationOverride() throws Exception {
|
public void registrationNonServletBean() throws Exception {
|
||||||
this.context = new AnnotationConfigWebApplicationContext();
|
this.context = new AnnotationConfigWebApplicationContext();
|
||||||
this.context.register(CustomDispatcherRegistration.class,
|
this.context.register(NonServletConfiguration.class,
|
||||||
|
ServerPropertiesAutoConfiguration.class,
|
||||||
|
DispatcherServletAutoConfiguration.class);
|
||||||
|
this.context.setServletContext(new MockServletContext());
|
||||||
|
this.context.refresh();
|
||||||
|
assertThat(this.context.getBeanNamesForType(ServletRegistrationBean.class).length)
|
||||||
|
.isEqualTo(0);
|
||||||
|
assertThat(this.context.getBeanNamesForType(DispatcherServlet.class).length)
|
||||||
|
.isEqualTo(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If a DispatcherServlet instance is registered with a name different
|
||||||
|
// from the default one, we're registering one anyway
|
||||||
|
@Test
|
||||||
|
public void registrationOverrideWithDispatcherServletWrongName() throws Exception {
|
||||||
|
this.context = new AnnotationConfigWebApplicationContext();
|
||||||
|
this.context.register(CustomDispatcherServletWrongName.class,
|
||||||
ServerPropertiesAutoConfiguration.class,
|
ServerPropertiesAutoConfiguration.class,
|
||||||
DispatcherServletAutoConfiguration.class);
|
DispatcherServletAutoConfiguration.class);
|
||||||
this.context.setServletContext(new MockServletContext());
|
this.context.setServletContext(new MockServletContext());
|
||||||
this.context.refresh();
|
this.context.refresh();
|
||||||
ServletRegistrationBean registration = this.context
|
ServletRegistrationBean registration = this.context
|
||||||
.getBean(ServletRegistrationBean.class);
|
.getBean(ServletRegistrationBean.class);
|
||||||
assertThat(registration.getUrlMappings().toString()).isEqualTo("[/foo]");
|
assertThat(registration.getUrlMappings().toString()).isEqualTo("[/]");
|
||||||
assertThat(registration.getServletName()).isEqualTo("customDispatcher");
|
assertThat(registration.getServletName()).isEqualTo("dispatcherServlet");
|
||||||
assertThat(this.context.getBeanNamesForType(DispatcherServlet.class).length)
|
assertThat(this.context.getBeanNamesForType(DispatcherServlet.class).length)
|
||||||
.isEqualTo(0);
|
.isEqualTo(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If you override either the dispatcherServlet or its registration you have to
|
@Test
|
||||||
// provide both...
|
|
||||||
@Test(expected = UnsatisfiedDependencyException.class)
|
|
||||||
public void registrationOverrideWithAutowiredServlet() throws Exception {
|
public void registrationOverrideWithAutowiredServlet() throws Exception {
|
||||||
this.context = new AnnotationConfigWebApplicationContext();
|
this.context = new AnnotationConfigWebApplicationContext();
|
||||||
this.context.register(CustomAutowiredRegistration.class,
|
this.context.register(CustomAutowiredRegistration.class,
|
||||||
@@ -199,14 +213,11 @@ public class DispatcherServletAutoConfigurationTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
protected static class CustomDispatcherRegistration {
|
protected static class CustomDispatcherServletWrongName {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public ServletRegistrationBean dispatcherServletRegistration() {
|
public DispatcherServlet customDispatcherServlet() {
|
||||||
ServletRegistrationBean registration = new ServletRegistrationBean(
|
return new DispatcherServlet();
|
||||||
new DispatcherServlet(), "/foo");
|
|
||||||
registration.setName("customDispatcher");
|
|
||||||
return registration;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -225,6 +236,15 @@ public class DispatcherServletAutoConfigurationTests {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
protected static class NonServletConfiguration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public String dispatcherServlet() {
|
||||||
|
return "spring";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
protected static class MultipartResolverConfiguration {
|
protected static class MultipartResolverConfiguration {
|
||||||
|
|
||||||
|
|||||||
@@ -217,7 +217,7 @@ public class EmbeddedServletContainerAutoConfigurationTests {
|
|||||||
return new DispatcherServlet();
|
return new DispatcherServlet();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean(name = DispatcherServletAutoConfiguration.DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
|
||||||
public ServletRegistrationBean dispatcherRegistration() {
|
public ServletRegistrationBean dispatcherRegistration() {
|
||||||
return new ServletRegistrationBean(dispatcherServlet(), "/app/*");
|
return new ServletRegistrationBean(dispatcherServlet(), "/app/*");
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user