Introduce ContextLoader(Listener) constructors

Constructors have been added to both ContextLoader and
ContextLoaderListener to support instance-based programmatic
registration of listeners within ServletContainerInitializer
implementations in Servlet 3.0+ environments, and more particularly
when using Spring 3.1's WebApplicationInitializer SPI.

Issue: SPR-7672
This commit is contained in:
Chris Beams 2011-05-30 12:56:01 +00:00
parent 2b4328023e
commit e36b5e9b7e
2 changed files with 167 additions and 22 deletions

View File

@ -37,7 +37,6 @@ import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.access.ContextSingletonBeanFactoryLocator;
import org.springframework.core.GenericTypeResolver;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.support.PropertiesLoaderUtils;
@ -76,6 +75,11 @@ import org.springframework.util.StringUtils;
* the root application context. See the
* {@link #loadParentContext(ServletContext)} method for more information.
*
* <p>As of Spring 3.1, {@code ContextLoader} supports injecting the root web
* application context via the {@link #ContextLoader(WebApplicationContext)}
* constructor, allowing for programmatic configuration in Servlet 3.0+ environments. See
* {@link org.springframework.web.WebApplicationInitializer} for usage examples.
*
* @author Juergen Hoeller
* @author Colin Sampaleanu
* @author Sam Brannen
@ -184,12 +188,69 @@ public class ContextLoader {
private BeanFactoryReference parentContextRef;
/**
* Create a new {@code ContextLoader} that will create a web application context
* based on the "contextClass" and "contextConfigLocation" servlet context-params.
* See class-level documentation for details on default values for each.
* <p>This constructor is typically used when declaring the {@code
* ContextLoaderListener} subclass as a {@code <listener>} within {@code web.xml}, as
* a no-arg constructor is required.
* <p>The created application context will be registered into the ServletContext under
* the attribute name {@link WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE}
* and subclasses are free to call the {@link #closeWebApplicationContext} method on
* container shutdown to close the application context.
* @see #ContextLoader(WebApplicationContext)
* @see #initWebApplicationContext(ServletContext)
* @see #closeWebApplicationContext(ServletContext)
*/
public ContextLoader() {
}
/**
* Create a new {@code ContextLoader} with the given application context. This
* constructor is useful in Servlet 3.0+ environments where instance-based
* registration of listeners is possible through the {@link ServletContext#addListener}
* API.
* <p>The context may or may not yet be {@linkplain
* ConfigurableApplicationContext#refresh() refreshed}. If it (a) is an implementation
* of {@link ConfigurableWebApplicationContext} and (b) has <strong>not</strong>
* already been refreshed (the recommended approach), then the following will occur:
* <ul>
* <li>If the given context has not already been assigned an {@linkplain
* ConfigurableApplicationContext#setId id}, one will be assigned to it</li>
* <li>{@code ServletContext} and {@code ServletConfig} objects will be delegated to
* the application context</li>
* <li>{@link #customizeContext} will be called</li>
* <li>Any {@link ApplicationContextInitializer}s specified through the
* "contextInitializerClasses" init-param will be applied.</li>
* <li>{@link ConfigurableApplicationContext#refresh refresh()} will be called</li>
* </ul>
* If the context has already been refreshed or does not implement
* {@code ConfigurableWebApplicationContext}, none of the above will occur under the
* assumption that the user has performed these actions (or not) per his or her
* specific needs.
* <p>See {@link org.springframework.web.WebApplicationInitializer} for usage examples.
* <p>In any case, the given application context will be registered into the
* ServletContext under the attribute name {@link
* WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE} and subclasses are
* free to call the {@link #closeWebApplicationContext} method on container shutdown
* to close the application context.
* @param context the application context to manage
* @see #initWebApplicationContext(ServletContext)
* @see #closeWebApplicationContext(ServletContext)
*/
public ContextLoader(WebApplicationContext context) {
this.context = context;
}
/**
* Initialize Spring's web application context for the given servlet context,
* using the application context provided at construction time, or creating a new one
* according to the "{@link #CONTEXT_CLASS_PARAM contextClass}" and
* "{@link #CONFIG_LOCATION_PARAM contextConfigLocation}" context-params.
* @param servletContext current servlet context
* @return the new WebApplicationContext
* @see #ContextLoader(WebApplicationContext)
* @see #CONTEXT_CLASS_PARAM
* @see #CONFIG_LOCATION_PARAM
*/
@ -208,12 +269,14 @@ public class ContextLoader {
long startTime = System.currentTimeMillis();
try {
// Determine parent for root web application context, if any.
ApplicationContext parent = loadParentContext(servletContext);
// Store context in local instance variable, to guarantee that
// it is available on ServletContext shutdown.
this.context = createWebApplicationContext(servletContext, parent);
if (this.context == null) {
this.context = createWebApplicationContext(servletContext);
}
if (this.context instanceof ConfigurableWebApplicationContext) {
configureAndRefreshWebApplicationContext((ConfigurableWebApplicationContext)this.context, servletContext);
}
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
ClassLoader ccl = Thread.currentThread().getContextClassLoader();
@ -260,7 +323,7 @@ public class ContextLoader {
* @return the root WebApplicationContext
* @see ConfigurableWebApplicationContext
*/
protected WebApplicationContext createWebApplicationContext(ServletContext sc, ApplicationContext parent) {
protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
Class<?> contextClass = determineContextClass(sc);
if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
throw new ApplicationContextException("Custom context class [" + contextClass.getName() +
@ -268,32 +331,50 @@ public class ContextLoader {
}
ConfigurableWebApplicationContext wac =
(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
return wac;
}
// Assign the best possible id value.
if (sc.getMajorVersion() == 2 && sc.getMinorVersion() < 5) {
// Servlet <= 2.4: resort to name specified in web.xml, if any.
String servletContextName = sc.getServletContextName();
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
ObjectUtils.getDisplayString(servletContextName));
}
else {
// Servlet 2.5's getContextPath available!
try {
String contextPath = (String) ServletContext.class.getMethod("getContextPath").invoke(sc);
/**
* @deprecated as of Spring 3.1 in favor of
* {@link #createWebApplicationContext(ServletContext)} and
* {@link #configureAndRefreshWebApplicationContext(WebApplicationContext, ServletContext)}
*/
@Deprecated
protected WebApplicationContext createWebApplicationContext(ServletContext sc, ApplicationContext parent) {
return createWebApplicationContext(sc);
}
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
// The application context id is still set to its original default value
// -> assign a more useful id based on available information
if (sc.getMajorVersion() == 2 && sc.getMinorVersion() < 5) {
// Servlet <= 2.4: resort to name specified in web.xml, if any.
String servletContextName = sc.getServletContextName();
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
ObjectUtils.getDisplayString(contextPath));
ObjectUtils.getDisplayString(servletContextName));
}
catch (Exception ex) {
throw new IllegalStateException("Failed to invoke Servlet 2.5 getContextPath method", ex);
else {
// Servlet 2.5's getContextPath available!
try {
String contextPath = (String) ServletContext.class.getMethod("getContextPath").invoke(sc);
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
ObjectUtils.getDisplayString(contextPath));
}
catch (Exception ex) {
throw new IllegalStateException("Failed to invoke Servlet 2.5 getContextPath method", ex);
}
}
}
// Determine parent for root web application context, if any.
ApplicationContext parent = loadParentContext(sc);
wac.setParent(parent);
wac.setServletContext(sc);
wac.setConfigLocation(sc.getInitParameter(CONFIG_LOCATION_PARAM));
customizeContext(sc, wac);
wac.refresh();
return wac;
}
/**

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2009 the original author or authors.
* 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.
@ -27,8 +27,15 @@ import javax.servlet.ServletContextListener;
* {@link org.springframework.web.util.Log4jConfigListener}
* in <code>web.xml</code>, if the latter is used.
*
* <p>As of Spring 3.1, {@code ContextLoaderListener} supports injecting the root web
* application context via the {@link #ContextLoaderListener(WebApplicationContext)}
* constructor, allowing for programmatic configuration in Servlet 3.0+ environments. See
* {@link org.springframework.web.WebApplicationInitializer} for usage examples.
*
* @author Juergen Hoeller
* @author Chris Beams
* @since 17.02.2003
* @see org.springframework.web.WebApplicationInitializer
* @see org.springframework.web.util.Log4jConfigListener
*/
public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
@ -36,6 +43,63 @@ public class ContextLoaderListener extends ContextLoader implements ServletConte
private ContextLoader contextLoader;
/**
* Create a new {@code ContextLoaderListener} that will create a web application
* context based on the "contextClass" and "contextConfigLocation" servlet
* context-params. See {@link ContextLoader} superclass documentation for details on
* default values for each.
* <p>This constructor is typically used when declaring {@code ContextLoaderListener}
* as a {@code <listener>} within {@code web.xml}, where a no-arg constructor is
* required.
* <p>The created application context will be registered into the ServletContext under
* the attribute name {@link WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE}
* and the Spring application context will be closed when the {@link #contextDestroyed}
* lifecycle method is invoked on this listener.
* @see ContextLoader
* @see #ContextLoaderListener(WebApplicationContext)
* @see #contextInitialized(ServletContextEvent)
* @see #contextDestroyed(ServletContextEvent)
*/
public ContextLoaderListener() {
}
/**
* Create a new {@code ContextLoaderListener} with the given application context. This
* constructor is useful in Servlet 3.0+ environments where instance-based
* registration of listeners is possible through the {@link ServletContext#addListener}
* API.
* <p>The context may or may not yet be {@linkplain
* ConfigurableApplicationContext#refresh() refreshed}. If it (a) is an implementation
* of {@link ConfigurableWebApplicationContext} and (b) has <strong>not</strong>
* already been refreshed (the recommended approach), then the following will occur:
* <ul>
* <li>If the given context has not already been assigned an {@linkplain
* ConfigurableApplicationContext#setId id}, one will be assigned to it</li>
* <li>{@code ServletContext} and {@code ServletConfig} objects will be delegated to
* the application context</li>
* <li>{@link #customizeContext} will be called</li>
* <li>Any {@link ApplicationContextInitializer}s specified through the
* "contextInitializerClasses" init-param will be applied.</li>
* <li>{@link ConfigurableApplicationContext#refresh refresh()} will be called</li>
* </ul>
* If the context has already been refreshed or does not implement
* {@code ConfigurableWebApplicationContext}, none of the above will occur under the
* assumption that the user has performed these actions (or not) per his or her
* specific needs.
* <p>See {@link org.springframework.web.WebApplicationInitializer} for usage examples.
* <p>In any case, the given application context will be registered into the
* ServletContext under the attribute name {@link
* WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE} and the Spring
* application context will be closed when the {@link #contextDestroyed} lifecycle
* method is invoked on this listener.
* @param context the application context to manage
* @see #contextInitialized(ServletContextEvent)
* @see #contextDestroyed(ServletContextEvent)
*/
public ContextLoaderListener(WebApplicationContext context) {
super(context);
}
/**
* Initialize the root web application context.
*/