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 @@
@@ -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 @@
@@ -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