diff --git a/org.springframework.web/src/main/java/org/springframework/web/SpringServletContainerInitializer.java b/org.springframework.web/src/main/java/org/springframework/web/SpringServletContainerInitializer.java new file mode 100644 index 00000000000..5ea1ebfb1e3 --- /dev/null +++ b/org.springframework.web/src/main/java/org/springframework/web/SpringServletContainerInitializer.java @@ -0,0 +1,165 @@ +/* + * Copyright 2002-2011 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.web; + +import static org.springframework.beans.BeanUtils.instantiateClass; + +import java.lang.reflect.Modifier; +import java.util.ServiceLoader; +import java.util.Set; +import java.util.TreeSet; + +import javax.servlet.ServletContainerInitializer; +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.annotation.HandlesTypes; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.core.annotation.AnnotationAwareOrderComparator; + +/** + * Servlet 3.0 {@link ServletContainerInitializer} designed to support code-based + * configuration of the servlet container using Spring's {@link WebApplicationInitializer} + * SPI as opposed to (or possibly in combination with) the traditional + * {@code web.xml}-based approach. + * + *
If a web application does include a {@code WEB-INF/web.xml} file, it is important to
+ * understand that neither this nor any other {@code ServletContextInitializer} will be
+ * processed unless the {@code Note that use of this container initializer and of {@code WebApplicationInitializer}
+ * is not in any way "tied" to Spring MVC other than the fact that the types are shipped
+ * in the {@code spring-web} module JAR. Rather, they can be considered general-purpose
+ * in their ability to facilitate convenient code-based configuration of the
+ * {@code ServletContext}. Said another way, any servlet, listener, or filter may be
+ * registered within a {@code WebApplicationInitializer}, not just Spring MVC-specific
+ * components.
+ *
+ * This class is not designed for nor intended to be extended. It should be considered
+ * an internal type, with {@code WebApplicationInitializer} being the public-facing SPI.
+ *
+ *
+ *
+ * @author Chris Beams
+ * @since 3.1
+ * @see #onStartup(Set, ServletContext)
+ * @see WebApplicationInitializer
+ */
+@HandlesTypes(WebApplicationInitializer.class)
+public class SpringServletContainerInitializer implements ServletContainerInitializer {
+
+ private static final Log logger = LogFactory.getLog(SpringServletContainerInitializer.class);
+
+ /**
+ * Delegate the {@code ServletContext} to any {@link WebApplicationInitializer}
+ * implementations present on the application classpath.
+ *
+ * Because this class declares @{@code HandlesTypes(WebApplicationInitializer.class)},
+ * Servlet 3.0+ containers will automatically scan the classpath for implementations
+ * of Spring's {@code WebApplicationInitializer} interface and provide the set of all
+ * such types to the {@code webAppInitializerClasses} parameter of this method.
+ *
+ * If no {@code WebApplicationInitializer} implementations are found on the
+ * classpath, this method is effectively a no-op. An INFO-level log message will be
+ * issued notifying the user that the {@code ServletContainerInitializer} has indeed
+ * been invoked, but that no {@code WebApplicationInitializer} implementations were
+ * found.
+ *
+ * Assuming that one or more {@code WebApplicationInitializer} types are detected,
+ * they will be instantiated (and sorted if the @{@link
+ * org.springframework.core.annotation.Order Order} annotation is present or
+ * the {@link org.springframework.core.Ordered Ordered} interface has been
+ * implemented). Then the {@link WebApplicationInitializer#onStartup(ServletContext)}
+ * method will be invoked on each instance, delegating the {@code ServletContext} such
+ * that each instance may register and configure servlets such as Spring's
+ * {@code DispatcherServlet}, listeners such as Spring's {@code ContextLoaderListener}
+ * or any other Servlet API componentry such as filters.
+ *
+ * @param webAppInitializerClasses all implementations of
+ * {@link WebApplicationInitializer} found on the application classpath.
+ * @param servletContext the servlet context to be initialized
+ * @see WebApplicationInitializer#onStartup(ServletContext)
+ * @see AnnotationAwareOrderComparator
+ */
+ public void onStartup(Set Implementations of this SPI will be detected automatically by the
+ * {@link SpringServletContainerInitializer}, which itself is automatically bootstrapped
+ * by Servlet 3.0 container. See {@linkplain SpringServletContainerInitializer its Javadoc}
+ * for details on this bootstrapping mechanism.
+ *
+ * This style is both simpler and more concise. There is no concern for dealing with
+ * init-params, etc, just normal JavaBean-style properties and constructor arguments. You
+ * are free to create and work with your Spring application contexts as necessary before
+ * injecting them into the {@code DispatcherServlet}.
+ *
+ * Most major Spring Web componentry has been updated to support this style of
+ * registration. You'll find that {@code DispatcherServlet}, {@code FrameworkServlet},
+ * {@code ContextLoaderListener} and {@code DelegatingFilterProxy} all now support
+ * constructor arguments. Even if a component (e.g. non-Spring, other third party) has not
+ * been specifically updated for use within {@code WebApplicationInitializers}, they still
+ * may be used in any case. The Servlet 3.0 {@code ServletContext} API allows for setting
+ * init-params, context-params, etc programmatically.
+ *
+ * {@code WEB-INF/web.xml} and {@code WebApplicationInitializer} use are not mutually
+ * exclusive; for example, web.xml can register one servlet, and a {@code
+ * WebApplicationInitializer} can register another. An initializer can even
+ * modify registrations performed in {@code web.xml} through the
+ * {@link ServletContext#getServletRegistration(String)} method and its similiars.
+ * However, if {@code WEB-INF/web.xml} is present in the application, its
+ * {@code version} attribute must be set to "3.0" or greater, otherwise {@code
+ * ServletContainerInitializer} bootstrapping will be ignored by the servlet container.
+ *
+ *
+ * Apache Tomcat maps its internal {@code DefaultServlet} to "/", and on Tomcat versions
+ * <= 7.0.14, this servlet mapping cannot be overridden programmatically. This
+ * is a known issue
+ * and scheduled to be addressed in the next revision of Tomcat. Overriding the "/"
+ * servlet mapping has been tested successfully on GlassFish 3.1.
+ *
+ * @author Chris Beams
+ * @since 3.1
+ * @see SpringServletContainerInitializer
+ */
+public interface WebApplicationInitializer {
+
+ /**
+ * Configure the given {@link ServletContext} with any servlets, filters, listeners
+ * context-params and attributes necessary for initializing this web application. See
+ * examples {@linkplain WebApplicationInitializer above}.
+ * @param servletContext the {@code ServletContext} to initialize
+ * @throws ServletException if any call against the given {@code ServletContext}
+ * throws a {@code ServletException}
+ */
+ void onStartup(ServletContext servletContext) throws ServletException;
+
+}
diff --git a/org.springframework.web/src/main/java/org/springframework/web/context/support/AnnotationConfigWebApplicationContext.java b/org.springframework.web/src/main/java/org/springframework/web/context/support/AnnotationConfigWebApplicationContext.java
index 2b0a61d5150..c8ecaafbd46 100644
--- a/org.springframework.web/src/main/java/org/springframework/web/context/support/AnnotationConfigWebApplicationContext.java
+++ b/org.springframework.web/src/main/java/org/springframework/web/context/support/AnnotationConfigWebApplicationContext.java
@@ -37,14 +37,19 @@ import org.springframework.web.context.ContextLoader;
* as for classpath scanning (specifying base packages as config location).
*
* This is essentially the equivalent of
- * {@link org.springframework.context.annotation.AnnotationConfigApplicationContext}
- * for a web environment.
+ * {@link org.springframework.context.annotation.AnnotationConfigApplicationContext
+ * AnnotationConfigApplicationContext} for a web environment.
*
* To make use of this application context, the
* {@linkplain ContextLoader#CONTEXT_CLASS_PARAM "contextClass"} context-param for
* ContextLoader and/or "contextClass" init-param for FrameworkServlet must be set to
* the fully-qualified name of this class.
*
+ * As of Spring 3.1, this class may also be directly instantiated and injected into
+ * Spring's {@code DispatcherServlet} or {@code ContextLoaderListener} when using the
+ * new {@link org.springframework.web.WebApplicationInitializer WebApplicationInitializer}
+ * code-based alternative to {@code web.xml}. See its Javadoc for details and usage examples.
+ *
* Unlike {@link XmlWebApplicationContext}, no default configuration class locations
* are assumed. Rather, it is a requirement to set the
* {@linkplain ContextLoader#CONFIG_LOCATION_PARAM "contextConfigLocation"}
diff --git a/org.springframework.web/src/main/resources/META-INF/services/javax.servlet.ServletContainerInitializer b/org.springframework.web/src/main/resources/META-INF/services/javax.servlet.ServletContainerInitializer
new file mode 100644
index 00000000000..b0ab3c3f006
--- /dev/null
+++ b/org.springframework.web/src/main/resources/META-INF/services/javax.servlet.ServletContainerInitializer
@@ -0,0 +1 @@
+org.springframework.web.SpringServletContainerInitializer
\ No newline at end of file
Relationship to Spring's {@code WebApplicationInitializer}
+ * Spring's {@code WebApplicationInitializer} SPI consists of just one method:
+ * {@link WebApplicationInitializer#onStartup(ServletContext)}. The signature is intentionally
+ * quite similar to {@link ServletContainerInitializer#onStartup(Set, ServletContext)}:
+ * simply put, {@code SpringServletContainerInitializer} is responsible for instantiating
+ * and delegating the {@code ServletContext} to any user-defined
+ * {@code WebApplicationInitializer} implementations. It is then the responsibility of
+ * each {@code WebApplicationInitializer} to do the actual work of initializing the
+ * {@code ServletContext}. The exact process of delegation is described in detail in the
+ * {@link #onStartup} documentation below.
+ *
+ * General Notes
+ * In general, this class should be viewed as supporting infrastructure for
+ * the more important and user-facing {@code WebApplicationInitializer} SPI. Taking
+ * advantage of this container initializer is also completely optional: while
+ * it is true that this initializer will be loaded and invoked under all Servlet 3.0+
+ * runtimes, it remains the user's choice whether to make any
+ * {@code WebApplicationInitializer} implementations available on the classpath. If no
+ * {@code WebApplicationInitializer} types are detected, this container initializer will
+ * have no effect.
+ *
+ * See Also
+ * See {@link WebApplicationInitializer} Javadoc for examples and detailed usage
+ * recommendations.Example
+ * The traditional, XML-based approach
+ * Most Spring users building a web application will need to register Spring's {@code
+ * DispatcherServlet}. For reference, in WEB-INF/web.xml, this would typically be done as
+ * follows:
+ *
+ * {@code
+ *
+ *
+ * The code-based approach with {@code WebApplicationInitializer}
+ * Here is the equivalent {@code DispatcherServlet} registration logic,
+ * {@code WebApplicationInitializer}-style:
+ *
+ * public class MyWebAppInitializer implements WebApplicationInitializer {
+ *
+ * @Override
+ * public void onStartup(ServletContext container) {
+ * XmlWebApplicationContext appContext = new XmlWebApplicationContext()
+ * appContext.setConfigLocation("/WEB-INF/spring/dispatcher-config.xml");
+ *
+ * ServletRegistration.Dynamic dispatcher =
+ * container.addServlet("dispatcher", new DispatcherServlet(appContext));
+ * dispatcher.setLoadOnStartup(1);
+ * dispatcher.addMapping("/main");
+ * }
+ *
+ * }
+ *
+ * As you can see, thanks to Servlet 3.0's new {@link ServletContext#addServlet} method
+ * we're actually registering an instance of the {@code DispatcherServlet}, and
+ * this means that the {@code DispatcherServlet} can now be treated like any other object
+ * -- receiving constructor injection of its application context in this case.
+ *
+ * A 100% code-based approach to configuration
+ * In the example above, {@code WEB-INF/web.xml} was successfully replaced with code in
+ * the form of a {@code WebApplicationInitializer}, but the actual
+ * {@code dispatcher-config.xml} Spring configuration remained XML-based.
+ * {@code WebApplicationInitializer} is a perfect fit for use with Spring's code-based
+ * {@code @Configuration} classes. See @{@link
+ * org.springframework.context.annotation.Configuration Configuration} Javadoc for
+ * complete details, but the following example demonstrates refactoring to use Spring's
+ * {@link org.springframework.web.context.support.AnnotationConfigWebApplicationContext
+ * AnnotationConfigWebApplicationContext} in lieu of {@code XmlWebApplicationContext}, and
+ * user-defined {@code @Configuration} classes {@code AppConfig} and
+ * {@code DispatcherConfig} instead of Spring XML files. This example also goes a bit
+ * beyond those above to demonstrate typical configuration of the 'root' application
+ * context and registration of the {@code ContextLoaderListener}:
+ *
+ * public class MyWebAppInitializer implements WebApplicationInitializer {
+ *
+ * @Override
+ * public void onStartup(ServletContext container) {
+ * // Create the 'root' Spring application context
+ * AnnotationConfigWebApplicationContext rootContext =
+ * new AnnotationConfigWebApplicationContext();
+ * rootContext.register(AppConfig.class);
+ *
+ * // Manage the lifecycle of the root application context
+ * container.addListener(new ContextLoaderListener(rootContext));
+ *
+ * // Create the dispatcher servlet's Spring application context
+ * AnnotationConfigWebApplicationContext dispatcherContext =
+ * new AnnotationConfigWebApplicationContext();
+ * dispatcherContext.register(DispatcherConfig.class);
+ *
+ * // Register and map the dispatcher servlet
+ * ServletRegistration.Dynamic dispatcher =
+ * container.addServlet("dispatcher", new DispatcherServlet(dispatcherContext));
+ * dispatcher.setLoadOnStartup(1);
+ * dispatcher.addMapping("/main");
+ * }
+ *
+ * }
+ *
+ * Remember that {@code WebApplicationInitializer} implementations are detected
+ * automatically -- so you are free to package them within your application as you
+ * see fit.
+ *
+ * Ordering {@code WebApplicationInitializer} execution
+ * {@code WebApplicationInitializer} implementations may optionally be annotated at the
+ * class level with Spring's @{@link org.springframework.core.annotation.Order Order}
+ * annotation or may implement Spring's {@link org.springframework.core.Ordered Ordered}
+ * interface. If so, the initializers will be ordered prior to invocation. This provides
+ * a mechanism for users to ensure the order in which servlet container initialization
+ * occurs. Use of this feature is expected to be rare, as typical applications will likely
+ * centralize all container initialization within a single {@code WebApplicationInitializer}.
+ *
+ * Caveats
+ *
+ * web.xml versioning
+ * Mapping to '/' under Tomcat
+ *