diff --git a/org.springframework.context/src/main/java/org/springframework/context/ApplicationContextInitializer.java b/org.springframework.context/src/main/java/org/springframework/context/ApplicationContextInitializer.java index 3ec99452410..aa50b4cc3a8 100644 --- a/org.springframework.context/src/main/java/org/springframework/context/ApplicationContextInitializer.java +++ b/org.springframework.context/src/main/java/org/springframework/context/ApplicationContextInitializer.java @@ -20,6 +20,12 @@ package org.springframework.context; * Callback interface for initializing a Spring {@link ConfigurableApplicationContext} * prior to being {@linkplain ConfigurableApplicationContext#refresh() refreshed}. * + *

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

{@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 { diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/FrameworkServlet.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/FrameworkServlet.java index 6d39d27b43a..7e4b5d58223 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/FrameworkServlet.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/FrameworkServlet.java @@ -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. * + *

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

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

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

The default implementation is empty. refresh() will * be called automatically after this method returns. + *

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) {