From 5b1503d07de339e726134ee3d5ec73ac23064e9e Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Thu, 19 Sep 2013 10:27:54 +0100 Subject: [PATCH] Extract DispatcherServletAutoConfiguration so it can be used independently --- .../DispatcherServletAutoConfiguration.java | 81 +++++++++++++++++++ ...ddedServletContainerAutoConfiguration.java | 49 ----------- .../web/WebMvcAutoConfiguration.java | 2 +- .../main/resources/META-INF/spring.factories | 1 + ...ervletContainerAutoConfigurationTests.java | 22 ++--- .../EmbeddedWebApplicationContext.java | 16 ++-- 6 files changed, 107 insertions(+), 64 deletions(-) create mode 100644 spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/DispatcherServletAutoConfiguration.java diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/DispatcherServletAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/DispatcherServletAutoConfiguration.java new file mode 100644 index 0000000000..2628136857 --- /dev/null +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/DispatcherServletAutoConfiguration.java @@ -0,0 +1,81 @@ +/* + * Copyright 2012-2013 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; + +import java.util.Arrays; + +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.SpringBootCondition; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ConditionContext; +import org.springframework.context.annotation.Conditional; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.Ordered; +import org.springframework.core.annotation.Order; +import org.springframework.core.type.AnnotatedTypeMetadata; +import org.springframework.web.servlet.DispatcherServlet; + +/** + * {@link EnableAutoConfiguration Auto-configuration} for an the Spring + * {@link DispatcherServlet} servlet containers. + * + * @author Phillip Webb + * @author Dave Syer + */ +@Order(Ordered.HIGHEST_PRECEDENCE) +@Configuration +@ConditionalOnClass(DispatcherServlet.class) +@AutoConfigureAfter(EmbeddedServletContainerAutoConfiguration.class) +public class DispatcherServletAutoConfiguration { + + /* + * The bean name for a DispatcherServlet that will be mapped to the root URL "/" + */ + public static final String DEFAULT_DISPATCHER_SERVLET_BEAN_NAME = "dispatcherServlet"; + + @Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME) + @Conditional(DefaultDispatcherServletCondition.class) + public DispatcherServlet dispatcherServlet() { + return new DispatcherServlet(); + } + + private static class DefaultDispatcherServletCondition extends SpringBootCondition { + + @Override + public Outcome getMatchOutcome(ConditionContext context, + AnnotatedTypeMetadata metadata) { + + ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); + String[] beans = beanFactory.getBeanNamesForType(DispatcherServlet.class, + false, false); + if (beans.length == 0) { + return Outcome.match("no DispatcherServlet found"); + } + if (Arrays.asList(beans).contains(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)) { + return Outcome.noMatch("found DispatcherServlet named " + + DEFAULT_DISPATCHER_SERVLET_BEAN_NAME); + } + return Outcome + .match("one or more DispatcherServlets found and none is named " + + DEFAULT_DISPATCHER_SERVLET_BEAN_NAME); + } + } + +} diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/EmbeddedServletContainerAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/EmbeddedServletContainerAutoConfiguration.java index 951912f4d1..72aba05b27 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/EmbeddedServletContainerAutoConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/EmbeddedServletContainerAutoConfiguration.java @@ -16,8 +16,6 @@ package org.springframework.boot.autoconfigure.web; -import java.util.Arrays; - import javax.servlet.Servlet; import org.apache.catalina.startup.Tomcat; @@ -34,24 +32,18 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.SearchStrategy; -import org.springframework.boot.autoconfigure.condition.SpringBootCondition; import org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration.EmbeddedServletContainerCustomizerBeanPostProcessorRegistrar; import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizerBeanPostProcessor; import org.springframework.boot.context.embedded.EmbeddedServletContainerFactory; -import org.springframework.boot.context.embedded.ServletContextInitializer; import org.springframework.boot.context.embedded.jetty.JettyEmbeddedServletContainerFactory; import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory; import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.ConditionContext; -import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; -import org.springframework.core.type.AnnotatedTypeMetadata; import org.springframework.core.type.AnnotationMetadata; -import org.springframework.web.servlet.DispatcherServlet; /** * {@link EnableAutoConfiguration Auto-configuration} for an embedded servlet containers. @@ -64,26 +56,6 @@ import org.springframework.web.servlet.DispatcherServlet; @Import(EmbeddedServletContainerCustomizerBeanPostProcessorRegistrar.class) public class EmbeddedServletContainerAutoConfiguration { - /* - * The bean name for a DispatcherServlet that will be mapped to the root URL "/" - */ - public static final String DEFAULT_DISPATCHER_SERVLET_BEAN_NAME = "dispatcherServlet"; - - /** - * Add the {@link DispatcherServlet} unless the user has defined their own - * {@link ServletContextInitializer}s. - */ - @ConditionalOnClass(DispatcherServlet.class) - public static class DispatcherServletConfiguration { - - @Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME) - @Conditional(DefaultServletCondition.class) - public DispatcherServlet dispatcherServlet() { - return new DispatcherServlet(); - } - - } - /** * Nested configuration for if Tomcat is being used. */ @@ -146,25 +118,4 @@ public class EmbeddedServletContainerAutoConfiguration { } } - private static class DefaultServletCondition extends SpringBootCondition { - - @Override - public Outcome getMatchOutcome(ConditionContext context, - AnnotatedTypeMetadata metadata) { - - ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); - String[] beans = beanFactory.getBeanNamesForType(DispatcherServlet.class, - false, false); - if (beans.length == 0) { - return Outcome.match("no DispatcherServlet found"); - } - if (Arrays.asList(beans).contains(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)) { - return Outcome.noMatch("found DispatcherServlet named " - + DEFAULT_DISPATCHER_SERVLET_BEAN_NAME); - } - return Outcome.match("multiple DispatcherServlets found and none is named " - + DEFAULT_DISPATCHER_SERVLET_BEAN_NAME); - } - } - } diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/WebMvcAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/WebMvcAutoConfiguration.java index da36556f12..930a1078da 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/WebMvcAutoConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/WebMvcAutoConfiguration.java @@ -71,7 +71,7 @@ import org.springframework.web.servlet.view.InternalResourceViewResolver; WebMvcConfigurerAdapter.class }) @ConditionalOnMissingBean(WebMvcConfigurationSupport.class) @Order(Ordered.HIGHEST_PRECEDENCE + 10) -@AutoConfigureAfter(EmbeddedServletContainerAutoConfiguration.class) +@AutoConfigureAfter(DispatcherServletAutoConfiguration.class) public class WebMvcAutoConfiguration { private static final String[] SERVLET_RESOURCE_LOCATIONS = { "/" }; diff --git a/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories b/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories index e11abf2556..ab86b5042b 100644 --- a/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories +++ b/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories @@ -14,6 +14,7 @@ org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\ org.springframework.boot.autoconfigure.reactor.ReactorAutoConfiguration,\ org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration,\ org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration,\ +org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration,\ org.springframework.boot.autoconfigure.web.ServerPropertiesAutoConfiguration,\ org.springframework.boot.autoconfigure.web.MultipartAutoConfiguration,\ org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration,\ diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/EmbeddedServletContainerAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/EmbeddedServletContainerAutoConfigurationTests.java index f3b7d7cfc9..c215b80196 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/EmbeddedServletContainerAutoConfigurationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/EmbeddedServletContainerAutoConfigurationTests.java @@ -49,7 +49,8 @@ public class EmbeddedServletContainerAutoConfigurationTests { public void createFromConfigClass() throws Exception { this.context = new AnnotationConfigEmbeddedWebApplicationContext( EmbeddedContainerConfiguration.class, - EmbeddedServletContainerAutoConfiguration.class); + EmbeddedServletContainerAutoConfiguration.class, + DispatcherServletAutoConfiguration.class); verifyContext(); } @@ -58,7 +59,8 @@ public class EmbeddedServletContainerAutoConfigurationTests { this.context = new AnnotationConfigEmbeddedWebApplicationContext( DispatcherServletConfiguration.class, EmbeddedContainerConfiguration.class, - EmbeddedServletContainerAutoConfiguration.class); + EmbeddedServletContainerAutoConfiguration.class, + DispatcherServletAutoConfiguration.class); verifyContext(); } @@ -66,7 +68,8 @@ public class EmbeddedServletContainerAutoConfigurationTests { public void contextAlreadyHasDispatcherServlet() throws Exception { this.context = new AnnotationConfigEmbeddedWebApplicationContext( SpringServletConfiguration.class, EmbeddedContainerConfiguration.class, - EmbeddedServletContainerAutoConfiguration.class); + EmbeddedServletContainerAutoConfiguration.class, + DispatcherServletAutoConfiguration.class); verifyContext(); assertEquals(2, this.context.getBeanNamesForType(DispatcherServlet.class).length); } @@ -76,7 +79,8 @@ public class EmbeddedServletContainerAutoConfigurationTests { this.context = new AnnotationConfigEmbeddedWebApplicationContext( EmbeddedContainerConfiguration.class, EnsureContainerHasNoServletContext.class, - EmbeddedServletContainerAutoConfiguration.class); + EmbeddedServletContainerAutoConfiguration.class, + DispatcherServletAutoConfiguration.class); verifyContext(); } @@ -85,17 +89,17 @@ public class EmbeddedServletContainerAutoConfigurationTests { this.context = new AnnotationConfigEmbeddedWebApplicationContext( EmbeddedContainerConfiguration.class, CallbackEmbeddedContainerCustomizer.class, - EmbeddedServletContainerAutoConfiguration.class); + EmbeddedServletContainerAutoConfiguration.class, + DispatcherServletAutoConfiguration.class); verifyContext(); assertEquals(9000, getContainerFactory().getPort()); } private void verifyContext() { MockEmbeddedServletContainerFactory containerFactory = getContainerFactory(); - Servlet servlet = this.context - .getBean( - EmbeddedServletContainerAutoConfiguration.DEFAULT_DISPATCHER_SERVLET_BEAN_NAME, - Servlet.class); + Servlet servlet = this.context.getBean( + DispatcherServletAutoConfiguration.DEFAULT_DISPATCHER_SERVLET_BEAN_NAME, + Servlet.class); verify(containerFactory.getServletContext()).addServlet("dispatcherServlet", servlet); } diff --git a/spring-boot/src/main/java/org/springframework/boot/context/embedded/EmbeddedWebApplicationContext.java b/spring-boot/src/main/java/org/springframework/boot/context/embedded/EmbeddedWebApplicationContext.java index 5e041864c8..30bc46b9c4 100644 --- a/spring-boot/src/main/java/org/springframework/boot/context/embedded/EmbeddedWebApplicationContext.java +++ b/spring-boot/src/main/java/org/springframework/boot/context/embedded/EmbeddedWebApplicationContext.java @@ -298,11 +298,17 @@ public class EmbeddedWebApplicationContext extends GenericWebApplicationContext * @param servletContext the operational servlet context */ protected void prepareEmbeddedWebApplicationContext(ServletContext servletContext) { - if (servletContext - .getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) { - throw new IllegalStateException( - "Cannot initialize context because there is already a root application context present - " - + "check whether you have multiple ServletContextInitializer!"); + Object rootContext = servletContext + .getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE); + if (rootContext != null) { + if (rootContext == this) { + throw new IllegalStateException( + "Cannot initialize context because there is already a root application context present - " + + "check whether you have multiple ServletContextInitializers!"); + } + else { + return; + } } Log logger = LogFactory.getLog(ContextLoader.class); servletContext.log("Initializing Spring embedded WebApplicationContext");