Commit 09166407 authored by Brian Clozel's avatar Brian Clozel Committed by Phillip Webb

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
parent d87287fe
...@@ -40,6 +40,7 @@ import org.springframework.context.annotation.Bean; ...@@ -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; ...@@ -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 { ...@@ -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 { ...@@ -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,57 +145,65 @@ public class DispatcherServletAutoConfiguration { ...@@ -118,57 +145,65 @@ public class DispatcherServletAutoConfiguration {
return registration; return registration;
} }
@Bean }
@ConditionalOnBean(MultipartResolver.class)
@ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME) @Order(Ordered.LOWEST_PRECEDENCE - 10)
public MultipartResolver multipartResolver(MultipartResolver resolver) { private static class DefaultDispatcherServletCondition extends SpringBootCondition {
// Detect if the user has created a MultipartResolver but named it incorrectly
return resolver; @Override
public ConditionOutcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) {
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
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) @Order(Ordered.LOWEST_PRECEDENCE - 10)
private static class DefaultDispatcherServletCondition extends SpringBootCondition { private static class DispatcherServletRegistrationCondition
extends SpringBootCondition {
@Override @Override
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); 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 { ...@@ -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; ...@@ -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; ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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/*");
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment