Support "contextInitializerClasses" init-param

FrameworkServlet now has support equivalent to ContextLoader and its
"contextInitializerClasses" context-param introduced in 3.1 M1.

This allows users to specify ApplicationContextInitializers at the root
(ContextLoader) level and/or at the DispatcherServlet level.

Issue: SPR-8366
This commit is contained in:
Chris Beams 2011-05-23 10:02:18 +00:00
parent 3c6254df90
commit 56720fc42c
2 changed files with 80 additions and 1 deletions

View File

@ -20,6 +20,12 @@ package org.springframework.context;
* Callback interface for initializing a Spring {@link ConfigurableApplicationContext}
* prior to being {@linkplain ConfigurableApplicationContext#refresh() refreshed}.
*
* <p>Typically used within web applications that require some programmatic initialization
* of the application context. For example, registering property sources or activating
* profiles against the {@linkplain ConfigurableApplicationContext#getEnvironment()
* context's environment}. See {@code ContextLoader} and {@code FrameworkServlet} support
* for declaring a "contextInitializerClasses" context-param and init-param, respectively.
*
* <p>{@code ApplicationContextInitializer} processors are encouraged to detect
* whether Spring's {@link org.springframework.core.Ordered Ordered} or
* {@link org.springframework.core.PriorityOrdered PriorityOrdered} interfaces are also
@ -29,6 +35,8 @@ package org.springframework.context;
* @since 3.1
* @see org.springframework.web.context.ContextLoader#customizeContext
* @see org.springframework.web.context.ContextLoader#CONTEXT_INITIALIZER_CLASSES_PARAM
* @see org.springframework.web.servlet.FrameworkServlet#setContextInitializerClasses
* @see org.springframework.web.servlet.FrameworkServlet#initializeWebApplicationContext
*/
public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {

View File

@ -27,6 +27,7 @@ import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.BeanUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextException;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.event.ContextRefreshedEvent;
@ -34,6 +35,8 @@ import org.springframework.context.event.SourceFilteringListener;
import org.springframework.context.i18n.LocaleContext;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.context.i18n.SimpleLocaleContext;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.context.ConfigurableWebApplicationContext;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.RequestAttributes;
@ -71,6 +74,16 @@ import org.springframework.web.util.WebUtils;
* {@link org.springframework.web.context.ConfigurableWebApplicationContext
* ConfigurableWebApplicationContext} SPI.
*
* <p>Accepts an optional "contextInitializerClasses" servlet init-param that
* specifies one or more {@link org.springframework.context.ApplicationContextInitializer
* ApplicationContextInitializer} classes. The managed web application context will be
* delegated to these initializers, allowing for additional programmatic configuration,
* e.g. adding property sources or activating profiles against the {@linkplain
* org.springframework.context.ConfigurableApplicationContext#getEnvironment() context's
* environment}. See also {@link org.springframework.web.context.ContextLoader} which
* supports a "contextInitializerClasses" context-param with identical semantics for
* the "root" web application context.
*
* <p>Passes a "contextConfigLocation" servlet init-param to the context instance,
* parsing it into potentially multiple file paths which can be separated by any
* number of commas and spaces, like "test-servlet.xml, myServlet.xml".
@ -90,9 +103,11 @@ import org.springframework.web.util.WebUtils;
* @author Rod Johnson
* @author Juergen Hoeller
* @author Sam Brannen
* @author Chris Beams
* @see #doService
* @see #setContextClass
* @see #setContextConfigLocation
* @see #setContextInitializerClasses
* @see #setNamespace
*/
@SuppressWarnings("serial")
@ -117,6 +132,12 @@ public abstract class FrameworkServlet extends HttpServletBean {
*/
public static final String SERVLET_CONTEXT_PREFIX = FrameworkServlet.class.getName() + ".CONTEXT.";
/**
* Any number of these characters are considered delimiters between
* multiple values in a single init-param String value.
* @see #initializeWebApplicationContext
*/
private String INIT_PARAM_DELIMITERS = ",; \t\n";
/** ServletContext attribute to find the WebApplicationContext in */
private String contextAttribute;
@ -151,6 +172,8 @@ public abstract class FrameworkServlet extends HttpServletBean {
/** Flag used to detect whether onRefresh has already been called */
private boolean refreshEventReceived = false;
private String contextInitializerClasses;
/**
* Set the name of the ServletContext attribute which should be used to retrieve the
@ -204,6 +227,15 @@ public abstract class FrameworkServlet extends HttpServletBean {
return (this.namespace != null ? this.namespace : getServletName() + DEFAULT_NAMESPACE_SUFFIX);
}
/**
* Specify the set of fully-qualified {@link ApplicationContextInitializer} class
* names, per the optional "contextInitializerClasses" servlet init-param.
* @see #createWebApplicationContext
*/
public void setContextInitializerClasses(String contextInitializerClasses) {
this.contextInitializerClasses = contextInitializerClasses;
}
/**
* Set the context config location explicitly, instead of relying on the default
* location built from the namespace. This location string can consist of
@ -442,6 +474,9 @@ public abstract class FrameworkServlet extends HttpServletBean {
wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));
postProcessWebApplicationContext(wac);
initializeWebApplicationContext(wac);
wac.refresh();
return wac;
@ -460,14 +495,50 @@ public abstract class FrameworkServlet extends HttpServletBean {
protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) {
return createWebApplicationContext((ApplicationContext) parent);
}
/**
* Delegate the WebApplicationContext before it is refreshed to any
* {@link ApplicationContextInitializer} instances specified by the
* "contextInitializerClasses" servlet init-param.
* <p>See also {@link #postProcessWebApplicationContext}, which is designed to allow
* subclasses (as opposed to end-users) to modify the application context, and is
* called immediately after this method.
* @param wac the configured WebApplicationContext (not refreshed yet)
* @see #createWebApplicationContext
* @see #postProcessWebApplicationContext
* @see ConfigurableWebApplicationContext#refresh()
*/
@SuppressWarnings("unchecked")
protected void initializeWebApplicationContext(ConfigurableWebApplicationContext wac) {
if (this.contextInitializerClasses != null) {
String[] initializerClassNames = StringUtils.tokenizeToStringArray(this.contextInitializerClasses, INIT_PARAM_DELIMITERS);
for(String initializerClassName : initializerClassNames) {
ApplicationContextInitializer<ConfigurableApplicationContext> initializer = null;
try {
Class<?> initializerClass = ClassUtils.forName(initializerClassName, wac.getClassLoader());
initializer = BeanUtils.instantiateClass(initializerClass, ApplicationContextInitializer.class);
} catch (Exception ex) {
throw new IllegalArgumentException(
String.format("Could not instantiate class [%s] specified via " +
"'contextInitializerClasses' init-param", initializerClassName), ex);
}
initializer.initialize(wac);
}
}
}
/**
* Post-process the given WebApplicationContext before it is refreshed
* and activated as context for this servlet.
* <p>The default implementation is empty. <code>refresh()</code> will
* be called automatically after this method returns.
* <p>Note that this method is designed to allow subclasses to modify the application
* context, while {@link #initializeWebApplicationContext} is designed to allow
* end-users to modify the context through the use of
* {@link ApplicationContextInitializer}s.
* @param wac the configured WebApplicationContext (not refreshed yet)
* @see #createWebApplicationContext
* @see #initializeWebApplicationContext
* @see ConfigurableWebApplicationContext#refresh()
*/
protected void postProcessWebApplicationContext(ConfigurableWebApplicationContext wac) {