Introduce Servlet 3.0 WebApplicationInitializer
WebApplicationInitializer provides a programmatic alternative to the traditional WEB-INF/web.xml servlet container deployment descriptor for Servlet API 3.0+ environments. This is done by building atop the new ServletContainerInitializer support in Servlet 3.0. See SpringServletContainerInitializer for complete details. And see WebApplicationInitializer Javadoc for typical usage examples. Issue: SPR-7672
This commit is contained in:
parent
2b51759113
commit
c9e67b2ef2
|
|
@ -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.
|
||||
*
|
||||
* <h2>Mechanism of Operation</h2>
|
||||
* This class will be loaded and instantiated and have its {@link #onStartup} method
|
||||
* invoked by any Servlet 3.0-compliant container during container startup assuming that
|
||||
* the {@code spring-web} module JAR is present on the classpath. This occurs through the
|
||||
* JAR Services API {@link ServiceLoader#load(Class)} method detecting the
|
||||
* {@code spring-web} module's {@code META-INF/services/javax.servlet.ServletContainerInitializer}
|
||||
* service provider configuration file. See the
|
||||
* <a href="http://download.oracle.com/javase/6/docs/technotes/guides/jar/jar.html#Service%20Provider">
|
||||
* JAR Services API documentation</a> as well as section <em>8.2.4</em> of the Servlet 3.0
|
||||
* Final Draft specification for complete details.
|
||||
*
|
||||
* <h3>when in combination with {@code web.xml}</h3>
|
||||
* <p>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 <web-app>} element's {@code version} attribute is >= "3.0"
|
||||
* and the {@code xsi:schemaLocation} for "http://java.sun.com/xml/ns/javaee" is set to
|
||||
* "http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd".
|
||||
*
|
||||
* <h2>Relationship to Spring's {@code WebApplicationInitializer}</h2>
|
||||
* 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.
|
||||
*
|
||||
* <h2>General Notes</h2>
|
||||
* In general, this class should be viewed as <em>supporting infrastructure</em> for
|
||||
* the more important and user-facing {@code WebApplicationInitializer} SPI. Taking
|
||||
* advantage of this container initializer is also completely <em>optional</em>: 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.
|
||||
*
|
||||
* <p>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.
|
||||
*
|
||||
* <p>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.
|
||||
*
|
||||
* <h2>See Also</h2>
|
||||
* See {@link WebApplicationInitializer} Javadoc for examples and detailed usage
|
||||
* recommendations.<p>
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* <p>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.
|
||||
*
|
||||
* <p>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.
|
||||
*
|
||||
* <p>Assuming that one or more {@code WebApplicationInitializer} types are detected,
|
||||
* they will be instantiated (and <em>sorted</em> 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<Class<?>> webAppInitializerClasses,
|
||||
ServletContext servletContext) throws ServletException {
|
||||
|
||||
Set<WebApplicationInitializer> initializers =
|
||||
new TreeSet<WebApplicationInitializer>(new AnnotationAwareOrderComparator());
|
||||
|
||||
for (Class<?> waiClass : webAppInitializerClasses) {
|
||||
if (Modifier.isAbstract(waiClass.getModifiers())) {
|
||||
// the class is not instantiable (i.e. abstract or an interface) -> skip it
|
||||
continue;
|
||||
}
|
||||
|
||||
initializers.add(instantiateClass(waiClass, WebApplicationInitializer.class));
|
||||
}
|
||||
|
||||
if (initializers.isEmpty()) {
|
||||
logger.info("Detected no WebApplicationInitializer types on the classpath: exiting.");
|
||||
return;
|
||||
}
|
||||
|
||||
logger.info("Delegating ServletContext to the following " +
|
||||
"WebApplicationInitializer instances: " + initializers);
|
||||
|
||||
for (WebApplicationInitializer initializer : initializers) {
|
||||
initializer.onStartup(servletContext);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,183 @@
|
|||
/*
|
||||
* 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 javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletException;
|
||||
|
||||
/**
|
||||
* Interface to be implemented in Servlet 3.0+ environments in order to configure the
|
||||
* {@link ServletContext} programmatically -- as opposed to (or possibly in conjunction
|
||||
* with) the traditional {@code web.xml}-based approach.
|
||||
*
|
||||
* <p>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.
|
||||
*
|
||||
* <h2>Example</h2>
|
||||
* <h3>The traditional, XML-based approach</h3>
|
||||
* 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:
|
||||
* <pre class="code">
|
||||
* {@code
|
||||
* <servlet>
|
||||
* <servlet-name>dispatcher</servlet-name>
|
||||
* <servlet-class>
|
||||
* org.springframework.web.servlet.DispatcherServlet
|
||||
* </servlet-class>
|
||||
* <init-param>
|
||||
* <param-name>contextConfigLocation</param-name>
|
||||
* <param-value>/WEB-INF/spring/dispatcher-config.xml</param-value>
|
||||
* </init-param>
|
||||
* <load-on-startup>1</load-on-startup>
|
||||
* </servlet>
|
||||
*
|
||||
* <servlet-mapping>
|
||||
* <servlet-name>dispatcher</servlet-name>
|
||||
* <url-pattern>/main</url-pattern>
|
||||
* </servlet-mapping>}</pre>
|
||||
*
|
||||
* <h3>The code-based approach with {@code WebApplicationInitializer}</h3>
|
||||
* Here is the equivalent {@code DispatcherServlet} registration logic,
|
||||
* {@code WebApplicationInitializer}-style:
|
||||
* <pre class="code">
|
||||
* 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");
|
||||
* }
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* As you can see, thanks to Servlet 3.0's new {@link ServletContext#addServlet} method
|
||||
* we're actually registering an <em>instance</em> 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.
|
||||
*
|
||||
* <p>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}.
|
||||
*
|
||||
* <p>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.
|
||||
*
|
||||
* <h2>A 100% code-based approach to configuration</h2>
|
||||
* 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}:
|
||||
* <pre class="code">
|
||||
* 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");
|
||||
* }
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* Remember that {@code WebApplicationInitializer} implementations are <em>detected
|
||||
* automatically</em> -- so you are free to package them within your application as you
|
||||
* see fit.
|
||||
*
|
||||
* <h2>Ordering {@code WebApplicationInitializer} execution</h2>
|
||||
* {@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}.
|
||||
*
|
||||
* <h2>Caveats</h2>
|
||||
*
|
||||
* <h3>web.xml versioning</h3>
|
||||
* <p>{@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
|
||||
* <em>modify</em> registrations performed in {@code web.xml} through the
|
||||
* {@link ServletContext#getServletRegistration(String)} method and its similiars.
|
||||
* <strong>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.
|
||||
* </strong>
|
||||
*
|
||||
* <h3>Mapping to '/' under Tomcat</h3>
|
||||
* <p>Apache Tomcat maps its internal {@code DefaultServlet} to "/", and on Tomcat versions
|
||||
* <= 7.0.14, this servlet mapping <em>cannot be overridden programmatically</em>. This
|
||||
* is a <a href="https://issues.apache.org/bugzilla/show_bug.cgi?id=51278">known issue</a>
|
||||
* and scheduled to be addressed in the next revision of Tomcat. Overriding the "/"
|
||||
* servlet mapping has been tested successfully on GlassFish 3.1.<p>
|
||||
*
|
||||
* @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;
|
||||
|
||||
}
|
||||
|
|
@ -37,14 +37,19 @@ import org.springframework.web.context.ContextLoader;
|
|||
* as for classpath scanning (specifying base packages as config location).
|
||||
*
|
||||
* <p>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.
|
||||
*
|
||||
* <p>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.
|
||||
*
|
||||
* <p>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.
|
||||
*
|
||||
* <p>Unlike {@link XmlWebApplicationContext}, no default configuration class locations
|
||||
* are assumed. Rather, it is a requirement to set the
|
||||
* {@linkplain ContextLoader#CONFIG_LOCATION_PARAM "contextConfigLocation"}
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
org.springframework.web.SpringServletContainerInitializer
|
||||
Loading…
Reference in New Issue