diff --git a/org.springframework.web/src/main/java/org/springframework/web/context/ContextLoader.java b/org.springframework.web/src/main/java/org/springframework/web/context/ContextLoader.java index 731e4b20478..5b5669f8c1d 100644 --- a/org.springframework.web/src/main/java/org/springframework/web/context/ContextLoader.java +++ b/org.springframework.web/src/main/java/org/springframework/web/context/ContextLoader.java @@ -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. * + *

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. + *

This constructor is typically used when declaring the {@code + * ContextLoaderListener} subclass as a {@code } within {@code web.xml}, as + * a no-arg constructor is required. + *

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. + *

The context may or may not yet be {@linkplain + * ConfigurableApplicationContext#refresh() refreshed}. If it (a) is an implementation + * of {@link ConfigurableWebApplicationContext} and (b) has not + * already been refreshed (the recommended approach), then the following will occur: + *

+ * 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. + *

See {@link org.springframework.web.WebApplicationInitializer} for usage examples. + *

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; } /** diff --git a/org.springframework.web/src/main/java/org/springframework/web/context/ContextLoaderListener.java b/org.springframework.web/src/main/java/org/springframework/web/context/ContextLoaderListener.java index b0140f08a85..28de0fa40ec 100644 --- a/org.springframework.web/src/main/java/org/springframework/web/context/ContextLoaderListener.java +++ b/org.springframework.web/src/main/java/org/springframework/web/context/ContextLoaderListener.java @@ -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 web.xml, if the latter is used. * + *

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. + *

This constructor is typically used when declaring {@code ContextLoaderListener} + * as a {@code } within {@code web.xml}, where a no-arg constructor is + * required. + *

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. + *

The context may or may not yet be {@linkplain + * ConfigurableApplicationContext#refresh() refreshed}. If it (a) is an implementation + * of {@link ConfigurableWebApplicationContext} and (b) has not + * already been refreshed (the recommended approach), then the following will occur: + *

+ * 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. + *

See {@link org.springframework.web.WebApplicationInitializer} for usage examples. + *

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. */