Browse Source
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 git-svn-id: https://src.springframework.org/svn/spring-framework/trunk@4377 50f2f4bb-b051-0410-bef5-90022cba6387pull/1/merge
4 changed files with 356 additions and 2 deletions
@ -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; |
||||||
|
|
||||||
|
} |
||||||
Loading…
Reference in new issue