Browse Source

Introduced "globalInitializerClasses" next to the existing "contextInitializerClasses", applying to FrameworkServlets as well

Issue: SPR-11314
pull/438/merge
Juergen Hoeller 11 years ago
parent
commit
91881ff036
  1. 192
      spring-web/src/main/java/org/springframework/web/context/ContextLoader.java
  2. 145
      spring-webmvc/src/main/java/org/springframework/web/servlet/FrameworkServlet.java
  3. 95
      spring-webmvc/src/test/java/org/springframework/web/context/ContextLoaderTests.java

192
spring-web/src/main/java/org/springframework/web/context/ContextLoader.java

@ -77,8 +77,8 @@ import org.springframework.util.StringUtils;
* *
* <p>As of Spring 3.1, {@code ContextLoader} supports injecting the root web * <p>As of Spring 3.1, {@code ContextLoader} supports injecting the root web
* application context via the {@link #ContextLoader(WebApplicationContext)} * application context via the {@link #ContextLoader(WebApplicationContext)}
* constructor, allowing for programmatic configuration in Servlet 3.0+ environments. See * constructor, allowing for programmatic configuration in Servlet 3.0+ environments.
* {@link org.springframework.web.WebApplicationInitializer} for usage examples. * See {@link org.springframework.web.WebApplicationInitializer} for usage examples.
* *
* @author Juergen Hoeller * @author Juergen Hoeller
* @author Colin Sampaleanu * @author Colin Sampaleanu
@ -90,6 +90,20 @@ import org.springframework.util.StringUtils;
*/ */
public class ContextLoader { public class ContextLoader {
/**
* Config param for the root WebApplicationContext id,
* to be used as serialization id for the underlying BeanFactory: {@value}
*/
public static final String CONTEXT_ID_PARAM = "contextId";
/**
* Name of servlet context parameter (i.e., {@value}) that can specify the
* config location for the root context, falling back to the implementation's
* default otherwise.
* @see org.springframework.web.context.support.XmlWebApplicationContext#DEFAULT_CONFIG_LOCATION
*/
public static final String CONFIG_LOCATION_PARAM = "contextConfigLocation";
/** /**
* Config param for the root WebApplicationContext implementation class to use: {@value} * Config param for the root WebApplicationContext implementation class to use: {@value}
* @see #determineContextClass(ServletContext) * @see #determineContextClass(ServletContext)
@ -98,25 +112,18 @@ public class ContextLoader {
public static final String CONTEXT_CLASS_PARAM = "contextClass"; public static final String CONTEXT_CLASS_PARAM = "contextClass";
/** /**
* Config param for the root WebApplicationContext id, * Config param for {@link ApplicationContextInitializer} classes to use
* to be used as serialization id for the underlying BeanFactory: {@value} * for initializing the root web application context: {@value}
*/
public static final String CONTEXT_ID_PARAM = "contextId";
/**
* Config param for which {@link ApplicationContextInitializer} classes to use
* for initializing the web application context: {@value}
* @see #customizeContext(ServletContext, ConfigurableWebApplicationContext) * @see #customizeContext(ServletContext, ConfigurableWebApplicationContext)
*/ */
public static final String CONTEXT_INITIALIZER_CLASSES_PARAM = "contextInitializerClasses"; public static final String CONTEXT_INITIALIZER_CLASSES_PARAM = "contextInitializerClasses";
/** /**
* Name of servlet context parameter (i.e., {@value}) that can specify the * Config param for global {@link ApplicationContextInitializer} classes to use
* config location for the root context, falling back to the implementation's * for initializing all web application contexts in the current application: {@value}
* default otherwise. * @see #customizeContext(ServletContext, ConfigurableWebApplicationContext)
* @see org.springframework.web.context.support.XmlWebApplicationContext#DEFAULT_CONFIG_LOCATION
*/ */
public static final String CONFIG_LOCATION_PARAM = "contextConfigLocation"; public static final String GLOBAL_INITIALIZER_CLASSES_PARAM = "globalInitializerClasses";
/** /**
* Optional servlet context parameter (i.e., "{@code locatorFactorySelector}") * Optional servlet context parameter (i.e., "{@code locatorFactorySelector}")
@ -146,6 +153,12 @@ public class ContextLoader {
*/ */
public static final String LOCATOR_FACTORY_KEY_PARAM = "parentContextKey"; public static final String LOCATOR_FACTORY_KEY_PARAM = "parentContextKey";
/**
* Any number of these characters are considered delimiters between
* multiple values in a single init-param String value.
*/
private static final String INIT_PARAM_DELIMITERS = ",; \t\n";
/** /**
* Name of the class path resource (relative to the ContextLoader class) * Name of the class path resource (relative to the ContextLoader class)
* that defines ContextLoader's default strategy names. * that defines ContextLoader's default strategy names.
@ -373,14 +386,71 @@ public class ContextLoader {
} }
wac.setServletContext(sc); wac.setServletContext(sc);
String initParameter = sc.getInitParameter(CONFIG_LOCATION_PARAM); String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
if (initParameter != null) { if (configLocationParam != null) {
wac.setConfigLocation(initParameter); wac.setConfigLocation(configLocationParam);
} }
// The wac environment's #initPropertySources will be called in any case when the context
// is refreshed; do it eagerly here to ensure servlet property sources are in place for
// use in any post-processing or initialization that occurs below prior to #refresh
ConfigurableEnvironment env = wac.getEnvironment();
if (env instanceof ConfigurableWebEnvironment) {
((ConfigurableWebEnvironment) env).initPropertySources(sc, null);
}
customizeContext(sc, wac); customizeContext(sc, wac);
wac.refresh(); wac.refresh();
} }
/**
* Customize the {@link ConfigurableWebApplicationContext} created by this
* ContextLoader after config locations have been supplied to the context
* but before the context is <em>refreshed</em>.
* <p>The default implementation {@linkplain #determineContextInitializerClasses(ServletContext)
* determines} what (if any) context initializer classes have been specified through
* {@linkplain #CONTEXT_INITIALIZER_CLASSES_PARAM context init parameters} and
* {@linkplain ApplicationContextInitializer#initialize invokes each} with the
* given web application context.
* <p>Any {@code ApplicationContextInitializers} implementing
* {@link org.springframework.core.Ordered Ordered} or marked with @{@link
* org.springframework.core.annotation.Order Order} will be sorted appropriately.
* @param sc the current servlet context
* @param wac the newly created application context
* @see #createWebApplicationContext(ServletContext, ApplicationContext)
* @see #CONTEXT_INITIALIZER_CLASSES_PARAM
* @see ApplicationContextInitializer#initialize(ConfigurableApplicationContext)
*/
protected void customizeContext(ServletContext sc, ConfigurableWebApplicationContext wac) {
List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>> initializerClasses =
determineContextInitializerClasses(sc);
if (initializerClasses.isEmpty()) {
// no ApplicationContextInitializers have been declared -> nothing to do
return;
}
ArrayList<ApplicationContextInitializer<ConfigurableApplicationContext>> initializerInstances =
new ArrayList<ApplicationContextInitializer<ConfigurableApplicationContext>>();
for (Class<ApplicationContextInitializer<ConfigurableApplicationContext>> initializerClass : initializerClasses) {
Class<?> initializerContextClass =
GenericTypeResolver.resolveTypeArgument(initializerClass, ApplicationContextInitializer.class);
if (initializerContextClass != null) {
Assert.isAssignable(initializerContextClass, wac.getClass(), String.format(
"Could not add context initializer [%s] since its generic parameter [%s] " +
"is not assignable from the type of application context used by this " +
"context loader [%s]: ", initializerClass.getName(), initializerContextClass.getName(),
wac.getClass().getName()));
}
initializerInstances.add(BeanUtils.instantiateClass(initializerClass));
}
AnnotationAwareOrderComparator.sort(initializerInstances);
for (ApplicationContextInitializer<ConfigurableApplicationContext> initializer : initializerInstances) {
initializer.initialize(wac);
}
}
/** /**
* Return the WebApplicationContext implementation class to use, either the * Return the WebApplicationContext implementation class to use, either the
* default XmlWebApplicationContext or a custom context class if specified. * default XmlWebApplicationContext or a custom context class if specified.
@ -418,80 +488,38 @@ public class ContextLoader {
* @param servletContext current servlet context * @param servletContext current servlet context
* @see #CONTEXT_INITIALIZER_CLASSES_PARAM * @see #CONTEXT_INITIALIZER_CLASSES_PARAM
*/ */
@SuppressWarnings("unchecked")
protected List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>> protected List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>>
determineContextInitializerClasses(ServletContext servletContext) { determineContextInitializerClasses(ServletContext servletContext) {
String classNames = servletContext.getInitParameter(CONTEXT_INITIALIZER_CLASSES_PARAM);
List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>> classes = List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>> classes =
new ArrayList<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>>(); new ArrayList<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>>();
if (classNames != null) {
for (String className : StringUtils.tokenizeToStringArray(classNames, ",")) {
try {
Class<?> clazz = ClassUtils.forName(className, ClassUtils.getDefaultClassLoader());
Assert.isAssignable(ApplicationContextInitializer.class, clazz,
"class [" + className + "] must implement ApplicationContextInitializer");
classes.add((Class<ApplicationContextInitializer<ConfigurableApplicationContext>>)clazz);
}
catch (ClassNotFoundException ex) {
throw new ApplicationContextException(
"Failed to load context initializer class [" + className + "]", ex);
}
}
}
return classes;
}
/** String globalClassNames = servletContext.getInitParameter(GLOBAL_INITIALIZER_CLASSES_PARAM);
* Customize the {@link ConfigurableWebApplicationContext} created by this if (globalClassNames != null) {
* ContextLoader after config locations have been supplied to the context for (String className : StringUtils.tokenizeToStringArray(globalClassNames, INIT_PARAM_DELIMITERS)) {
* but before the context is <em>refreshed</em>. classes.add(loadInitializerClass(className));
* <p>The default implementation {@linkplain #determineContextInitializerClasses(ServletContext) }
* determines} what (if any) context initializer classes have been specified through
* {@linkplain #CONTEXT_INITIALIZER_CLASSES_PARAM context init parameters} and
* {@linkplain ApplicationContextInitializer#initialize invokes each} with the
* given web application context.
* <p>Any {@code ApplicationContextInitializers} implementing
* {@link org.springframework.core.Ordered Ordered} or marked with @{@link
* org.springframework.core.annotation.Order Order} will be sorted appropriately.
* @param servletContext the current servlet context
* @param applicationContext the newly created application context
* @see #createWebApplicationContext(ServletContext, ApplicationContext)
* @see #CONTEXT_INITIALIZER_CLASSES_PARAM
* @see ApplicationContextInitializer#initialize(ConfigurableApplicationContext)
*/
protected void customizeContext(ServletContext servletContext, ConfigurableWebApplicationContext applicationContext) {
List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>> initializerClasses =
determineContextInitializerClasses(servletContext);
if (initializerClasses.isEmpty()) {
// no ApplicationContextInitializers have been declared -> nothing to do
return;
} }
Class<?> contextClass = applicationContext.getClass(); String localClassNames = servletContext.getInitParameter(CONTEXT_INITIALIZER_CLASSES_PARAM);
ArrayList<ApplicationContextInitializer<ConfigurableApplicationContext>> initializerInstances = if (localClassNames != null) {
new ArrayList<ApplicationContextInitializer<ConfigurableApplicationContext>>(); for (String className : StringUtils.tokenizeToStringArray(localClassNames, INIT_PARAM_DELIMITERS)) {
classes.add(loadInitializerClass(className));
for (Class<ApplicationContextInitializer<ConfigurableApplicationContext>> initializerClass : initializerClasses) {
Class<?> initializerContextClass =
GenericTypeResolver.resolveTypeArgument(initializerClass, ApplicationContextInitializer.class);
if (initializerContextClass != null) {
Assert.isAssignable(initializerContextClass, contextClass, String.format(
"Could not add context initializer [%s] as its generic parameter [%s] " +
"is not assignable from the type of application context used by this " +
"context loader [%s]: ", initializerClass.getName(), initializerContextClass.getName(),
contextClass.getName()));
} }
initializerInstances.add(BeanUtils.instantiateClass(initializerClass));
} }
ConfigurableEnvironment env = applicationContext.getEnvironment(); return classes;
if (env instanceof ConfigurableWebEnvironment) { }
((ConfigurableWebEnvironment) env).initPropertySources(servletContext, null);
}
AnnotationAwareOrderComparator.sort(initializerInstances); @SuppressWarnings("unchecked")
for (ApplicationContextInitializer<ConfigurableApplicationContext> initializer : initializerInstances) { private Class<ApplicationContextInitializer<ConfigurableApplicationContext>> loadInitializerClass(String className) {
initializer.initialize(applicationContext); try {
Class<?> clazz = ClassUtils.forName(className, ClassUtils.getDefaultClassLoader());
Assert.isAssignable(ApplicationContextInitializer.class, clazz);
return (Class<ApplicationContextInitializer<ConfigurableApplicationContext>>) clazz;
}
catch (ClassNotFoundException ex) {
throw new ApplicationContextException("Failed to load context initializer class [" + className + "]", ex);
} }
} }

145
spring-webmvc/src/main/java/org/springframework/web/servlet/FrameworkServlet.java

@ -20,7 +20,6 @@ import java.io.IOException;
import java.security.Principal; import java.security.Principal;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import javax.servlet.ServletContext; import javax.servlet.ServletContext;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
@ -40,14 +39,17 @@ import org.springframework.context.event.SourceFilteringListener;
import org.springframework.context.i18n.LocaleContext; import org.springframework.context.i18n.LocaleContext;
import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.context.i18n.SimpleLocaleContext; import org.springframework.context.i18n.SimpleLocaleContext;
import org.springframework.core.GenericTypeResolver;
import org.springframework.core.annotation.AnnotationAwareOrderComparator; import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils; import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils; import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.context.ConfigurableWebApplicationContext; import org.springframework.web.context.ConfigurableWebApplicationContext;
import org.springframework.web.context.ConfigurableWebEnvironment; import org.springframework.web.context.ConfigurableWebEnvironment;
import org.springframework.web.context.ContextLoader;
import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.RequestAttributes; import org.springframework.web.context.request.RequestAttributes;
@ -175,6 +177,13 @@ public abstract class FrameworkServlet extends HttpServletBean implements Applic
/** Explicit context config location */ /** Explicit context config location */
private String contextConfigLocation; private String contextConfigLocation;
/** Actual ApplicationContextInitializer instances to apply to the context */
private final ArrayList<ApplicationContextInitializer<ConfigurableApplicationContext>> contextInitializers =
new ArrayList<ApplicationContextInitializer<ConfigurableApplicationContext>>();
/** Comma-delimited ApplicationContextInitializer class names set through init param */
private String contextInitializerClasses;
/** Should we publish the context as a ServletContext attribute? */ /** Should we publish the context as a ServletContext attribute? */
private boolean publishContext = true; private boolean publishContext = true;
@ -199,13 +208,6 @@ public abstract class FrameworkServlet extends HttpServletBean implements Applic
/** Flag used to detect whether onRefresh has already been called */ /** Flag used to detect whether onRefresh has already been called */
private boolean refreshEventReceived = false; private boolean refreshEventReceived = false;
/** Comma-delimited ApplicationContextInitializer classnames set through init param */
private String contextInitializerClasses;
/** Actual ApplicationContextInitializer instances to apply to the context */
private ArrayList<ApplicationContextInitializer<ConfigurableApplicationContext>> contextInitializers =
new ArrayList<ApplicationContextInitializer<ConfigurableApplicationContext>>();
/** /**
* Create a new {@code FrameworkServlet} that will create its own internal web * Create a new {@code FrameworkServlet} that will create its own internal web
@ -340,13 +342,19 @@ public abstract class FrameworkServlet extends HttpServletBean implements Applic
} }
/** /**
* Specify the set of fully-qualified {@link ApplicationContextInitializer} class * Set the context config location explicitly, instead of relying on the default
* names, per the optional "contextInitializerClasses" servlet init-param. * location built from the namespace. This location string can consist of
* @see #configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext) * multiple locations separated by any number of commas and spaces.
* @see #applyInitializers(ConfigurableApplicationContext)
*/ */
public void setContextInitializerClasses(String contextInitializerClasses) { public void setContextConfigLocation(String contextConfigLocation) {
this.contextInitializerClasses = contextInitializerClasses; this.contextConfigLocation = contextConfigLocation;
}
/**
* Return the explicit context config location, if any.
*/
public String getContextConfigLocation() {
return this.contextConfigLocation;
} }
/** /**
@ -355,7 +363,6 @@ public abstract class FrameworkServlet extends HttpServletBean implements Applic
* @see #configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext) * @see #configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext)
* @see #applyInitializers(ConfigurableApplicationContext) * @see #applyInitializers(ConfigurableApplicationContext)
*/ */
@SuppressWarnings("unchecked")
public void setContextInitializers(ApplicationContextInitializer<ConfigurableApplicationContext>... contextInitializers) { public void setContextInitializers(ApplicationContextInitializer<ConfigurableApplicationContext>... contextInitializers) {
for (ApplicationContextInitializer<ConfigurableApplicationContext> initializer : contextInitializers) { for (ApplicationContextInitializer<ConfigurableApplicationContext> initializer : contextInitializers) {
this.contextInitializers.add(initializer); this.contextInitializers.add(initializer);
@ -363,19 +370,13 @@ public abstract class FrameworkServlet extends HttpServletBean implements Applic
} }
/** /**
* Set the context config location explicitly, instead of relying on the default * Specify the set of fully-qualified {@link ApplicationContextInitializer} class
* location built from the namespace. This location string can consist of * names, per the optional "contextInitializerClasses" servlet init-param.
* multiple locations separated by any number of commas and spaces. * @see #configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext)
*/ * @see #applyInitializers(ConfigurableApplicationContext)
public void setContextConfigLocation(String contextConfigLocation) {
this.contextConfigLocation = contextConfigLocation;
}
/**
* Return the explicit context config location, if any.
*/ */
public String getContextConfigLocation() { public void setContextInitializerClasses(String contextInitializerClasses) {
return this.contextConfigLocation; this.contextInitializerClasses = contextInitializerClasses;
} }
/** /**
@ -627,19 +628,16 @@ public abstract class FrameworkServlet extends HttpServletBean implements Applic
wac.setNamespace(getNamespace()); wac.setNamespace(getNamespace());
wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener())); wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));
// the wac environment's #initPropertySources will be called in any case when // The wac environment's #initPropertySources will be called in any case when the context
// the context is refreshed; do it eagerly here to ensure servlet property sources // is refreshed; do it eagerly here to ensure servlet property sources are in place for
// are in place for use in any post-processing or initialization that occurs // use in any post-processing or initialization that occurs below prior to #refresh
// below prior to #refresh
ConfigurableEnvironment env = wac.getEnvironment(); ConfigurableEnvironment env = wac.getEnvironment();
if (env instanceof ConfigurableWebEnvironment) { if (env instanceof ConfigurableWebEnvironment) {
((ConfigurableWebEnvironment)env).initPropertySources(getServletContext(), getServletConfig()); ((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());
} }
postProcessWebApplicationContext(wac); postProcessWebApplicationContext(wac);
applyInitializers(wac); applyInitializers(wac);
wac.refresh(); wac.refresh();
} }
@ -657,58 +655,75 @@ public abstract class FrameworkServlet extends HttpServletBean implements Applic
return createWebApplicationContext((ApplicationContext) parent); return createWebApplicationContext((ApplicationContext) parent);
} }
/**
* Post-process the given WebApplicationContext before it is refreshed
* and activated as context for this servlet.
* <p>The default implementation is empty. {@code refresh()} 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 #initWebApplicationContext} 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 #initWebApplicationContext
* @see ConfigurableWebApplicationContext#refresh()
*/
protected void postProcessWebApplicationContext(ConfigurableWebApplicationContext wac) {
}
/** /**
* Delegate the WebApplicationContext before it is refreshed to any * Delegate the WebApplicationContext before it is refreshed to any
* {@link ApplicationContextInitializer} instances specified by the * {@link ApplicationContextInitializer} instances specified by the
* "contextInitializerClasses" servlet init-param. * "contextInitializerClasses" servlet init-param.
* <p>See also {@link #postProcessWebApplicationContext}, which is designed to allow * <p>See also {@link #postProcessWebApplicationContext}, which is designed to allow
* subclasses (as opposed to end-users) to modify the application context, and is * subclasses (as opposed to end-users) to modify the application context, and is
* called immediately after this method. * called immediately before this method.
* @param wac the configured WebApplicationContext (not refreshed yet) * @param wac the configured WebApplicationContext (not refreshed yet)
* @see #createWebApplicationContext * @see #createWebApplicationContext
* @see #postProcessWebApplicationContext * @see #postProcessWebApplicationContext
* @see ConfigurableApplicationContext#refresh() * @see ConfigurableApplicationContext#refresh()
*/ */
@SuppressWarnings("unchecked")
protected void applyInitializers(ConfigurableApplicationContext wac) { protected void applyInitializers(ConfigurableApplicationContext wac) {
String globalClassNames = getServletContext().getInitParameter(ContextLoader.GLOBAL_INITIALIZER_CLASSES_PARAM);
if (globalClassNames != null) {
for (String className : StringUtils.tokenizeToStringArray(globalClassNames, INIT_PARAM_DELIMITERS)) {
this.contextInitializers.add(loadInitializer(className, wac));
}
}
if (this.contextInitializerClasses != null) { if (this.contextInitializerClasses != null) {
String[] initializerClassNames = for (String className : StringUtils.tokenizeToStringArray(this.contextInitializerClasses, INIT_PARAM_DELIMITERS)) {
StringUtils.tokenizeToStringArray(this.contextInitializerClasses, INIT_PARAM_DELIMITERS); this.contextInitializers.add(loadInitializer(className, wac));
for (String initializerClassName : initializerClassNames) {
ApplicationContextInitializer<ConfigurableApplicationContext> initializer;
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);
}
this.contextInitializers.add(initializer);
} }
} }
AnnotationAwareOrderComparator.sort(this.contextInitializers); AnnotationAwareOrderComparator.sort(this.contextInitializers);
for (ApplicationContextInitializer<ConfigurableApplicationContext> initializer : this.contextInitializers) { for (ApplicationContextInitializer<ConfigurableApplicationContext> initializer : this.contextInitializers) {
initializer.initialize(wac); initializer.initialize(wac);
} }
} }
/** @SuppressWarnings("unchecked")
* Post-process the given WebApplicationContext before it is refreshed private ApplicationContextInitializer<ConfigurableApplicationContext> loadInitializer(
* and activated as context for this servlet. String className, ConfigurableApplicationContext wac) {
* <p>The default implementation is empty. {@code refresh()} will try {
* be called automatically after this method returns. Class<?> initializerClass = ClassUtils.forName(className, wac.getClassLoader());
* <p>Note that this method is designed to allow subclasses to modify the application Class<?> initializerContextClass =
* context, while {@link #initWebApplicationContext} is designed to allow GenericTypeResolver.resolveTypeArgument(initializerClass, ApplicationContextInitializer.class);
* end-users to modify the context through the use of if (initializerContextClass != null) {
* {@link ApplicationContextInitializer}s. Assert.isAssignable(initializerContextClass, wac.getClass(), String.format(
* @param wac the configured WebApplicationContext (not refreshed yet) "Could not add context initializer [%s] since its generic parameter [%s] " +
* @see #createWebApplicationContext "is not assignable from the type of application context used by this " +
* @see #initWebApplicationContext "framework servlet [%s]: ", initializerClass.getName(), initializerContextClass.getName(),
* @see ConfigurableWebApplicationContext#refresh() wac.getClass().getName()));
*/ }
protected void postProcessWebApplicationContext(ConfigurableWebApplicationContext wac) { return BeanUtils.instantiateClass(initializerClass, ApplicationContextInitializer.class);
}
catch (Exception ex) {
throw new IllegalArgumentException(String.format("Could not instantiate class [%s] specified " +
"via 'contextInitializerClasses' init-param", className), ex);
}
} }
/** /**

95
spring-webmvc/src/test/java/org/springframework/web/context/ContextLoaderTests.java

@ -16,30 +16,17 @@
package org.springframework.web.context; package org.springframework.web.context;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import javax.servlet.ServletContext; import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener; import javax.servlet.ServletContextListener;
import org.junit.Test; import org.junit.Test;
import org.springframework.beans.BeansException; import org.springframework.beans.BeansException;
import org.springframework.tests.sample.beans.TestBean;
import org.springframework.beans.factory.BeanCreationException; import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.BeanDefinitionStoreException; import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.tests.sample.beans.LifecycleBean;
import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextException; import org.springframework.context.ApplicationContextException;
@ -50,11 +37,17 @@ import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.PropertySource; import org.springframework.core.env.PropertySource;
import org.springframework.mock.web.test.MockServletConfig; import org.springframework.mock.web.test.MockServletConfig;
import org.springframework.mock.web.test.MockServletContext; import org.springframework.mock.web.test.MockServletContext;
import org.springframework.tests.sample.beans.LifecycleBean;
import org.springframework.tests.sample.beans.TestBean;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import org.springframework.web.context.support.WebApplicationContextUtils;
import org.springframework.web.context.support.XmlWebApplicationContext; import org.springframework.web.context.support.XmlWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet; import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.SimpleWebApplicationContext; import org.springframework.web.servlet.SimpleWebApplicationContext;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
/** /**
* Tests for {@link ContextLoader} and {@link ContextLoaderListener}. * Tests for {@link ContextLoader} and {@link ContextLoaderListener}.
* *
@ -70,14 +63,14 @@ public final class ContextLoaderTests {
public void testContextLoaderListenerWithDefaultContext() { public void testContextLoaderListenerWithDefaultContext() {
MockServletContext sc = new MockServletContext(""); MockServletContext sc = new MockServletContext("");
sc.addInitParameter(ContextLoader.CONFIG_LOCATION_PARAM, sc.addInitParameter(ContextLoader.CONFIG_LOCATION_PARAM,
"/org/springframework/web/context/WEB-INF/applicationContext.xml " "/org/springframework/web/context/WEB-INF/applicationContext.xml " +
+ "/org/springframework/web/context/WEB-INF/context-addition.xml"); "/org/springframework/web/context/WEB-INF/context-addition.xml");
ServletContextListener listener = new ContextLoaderListener(); ServletContextListener listener = new ContextLoaderListener();
ServletContextEvent event = new ServletContextEvent(sc); ServletContextEvent event = new ServletContextEvent(sc);
listener.contextInitialized(event); listener.contextInitialized(event);
WebApplicationContext context = (WebApplicationContext) sc.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE); WebApplicationContext context = (WebApplicationContext) sc.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
assertTrue("Correct WebApplicationContext exposed in ServletContext", context instanceof XmlWebApplicationContext); assertTrue("Correct WebApplicationContext exposed in ServletContext", context instanceof XmlWebApplicationContext);
assertTrue(ContextLoader.getCurrentWebApplicationContext() instanceof XmlWebApplicationContext); assertTrue(WebApplicationContextUtils.getRequiredWebApplicationContext(sc) instanceof XmlWebApplicationContext);
LifecycleBean lb = (LifecycleBean) context.getBean("lifecycle"); LifecycleBean lb = (LifecycleBean) context.getBean("lifecycle");
assertTrue("Has father", context.containsBean("father")); assertTrue("Has father", context.containsBean("father"));
assertTrue("Has rod", context.containsBean("rod")); assertTrue("Has rod", context.containsBean("rod"));
@ -88,7 +81,7 @@ public final class ContextLoaderTests {
listener.contextDestroyed(event); listener.contextDestroyed(event);
assertTrue("Destroyed", lb.isDestroyed()); assertTrue("Destroyed", lb.isDestroyed());
assertNull(sc.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE)); assertNull(sc.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE));
assertNull(ContextLoader.getCurrentWebApplicationContext()); assertNull(WebApplicationContextUtils.getWebApplicationContext(sc));
} }
/** /**
@ -104,12 +97,12 @@ public final class ContextLoaderTests {
final MockServletContext sc = new MockServletContext(""); final MockServletContext sc = new MockServletContext("");
sc.addInitParameter(ContextLoader.CONFIG_LOCATION_PARAM, sc.addInitParameter(ContextLoader.CONFIG_LOCATION_PARAM,
"/org/springframework/web/context/WEB-INF/applicationContext.xml"); "/org/springframework/web/context/WEB-INF/applicationContext.xml");
final ServletContextListener listener = new ContextLoaderListener() { ServletContextListener listener = new ContextLoaderListener() {
@Override @Override
protected void customizeContext(ServletContext servletContext, ConfigurableWebApplicationContext applicationContext) { protected void customizeContext(ServletContext sc, ConfigurableWebApplicationContext wac) {
assertNotNull("The ServletContext should not be null.", servletContext); assertNotNull("The ServletContext should not be null.", sc);
assertEquals("Verifying that we received the expected ServletContext.", sc, servletContext); assertEquals("Verifying that we received the expected ServletContext.", sc, sc);
assertFalse("The ApplicationContext should not yet have been refreshed.", applicationContext.isActive()); assertFalse("The ApplicationContext should not yet have been refreshed.", wac.isActive());
buffer.append(expectedContents); buffer.append(expectedContents);
} }
}; };
@ -118,16 +111,45 @@ public final class ContextLoaderTests {
} }
@Test @Test
public void testContextLoaderListenerWithRegisteredContextInitializer() { public void testContextLoaderListenerWithLocalContextInitializers() {
MockServletContext sc = new MockServletContext(""); MockServletContext sc = new MockServletContext("");
sc.addInitParameter(ContextLoader.CONFIG_LOCATION_PARAM, sc.addInitParameter(ContextLoader.CONFIG_LOCATION_PARAM,
"org/springframework/web/context/WEB-INF/ContextLoaderTests-acc-context.xml"); "org/springframework/web/context/WEB-INF/ContextLoaderTests-acc-context.xml");
sc.addInitParameter(ContextLoader.CONTEXT_INITIALIZER_CLASSES_PARAM, sc.addInitParameter(ContextLoader.CONTEXT_INITIALIZER_CLASSES_PARAM, StringUtils.arrayToCommaDelimitedString(
StringUtils.arrayToCommaDelimitedString( new Object[] {TestContextInitializer.class.getName(), TestWebContextInitializer.class.getName()}));
new Object[]{TestContextInitializer.class.getName(), TestWebContextInitializer.class.getName()}));
ContextLoaderListener listener = new ContextLoaderListener(); ContextLoaderListener listener = new ContextLoaderListener();
listener.contextInitialized(new ServletContextEvent(sc)); listener.contextInitialized(new ServletContextEvent(sc));
WebApplicationContext wac = ContextLoaderListener.getCurrentWebApplicationContext(); WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext(sc);
TestBean testBean = wac.getBean(TestBean.class);
assertThat(testBean.getName(), equalTo("testName"));
assertThat(wac.getServletContext().getAttribute("initialized"), notNullValue());
}
@Test
public void testContextLoaderListenerWithGlobalContextInitializers() {
MockServletContext sc = new MockServletContext("");
sc.addInitParameter(ContextLoader.CONFIG_LOCATION_PARAM,
"org/springframework/web/context/WEB-INF/ContextLoaderTests-acc-context.xml");
sc.addInitParameter(ContextLoader.GLOBAL_INITIALIZER_CLASSES_PARAM, StringUtils.arrayToCommaDelimitedString(
new Object[] {TestContextInitializer.class.getName(), TestWebContextInitializer.class.getName()}));
ContextLoaderListener listener = new ContextLoaderListener();
listener.contextInitialized(new ServletContextEvent(sc));
WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext(sc);
TestBean testBean = wac.getBean(TestBean.class);
assertThat(testBean.getName(), equalTo("testName"));
assertThat(wac.getServletContext().getAttribute("initialized"), notNullValue());
}
@Test
public void testContextLoaderListenerWithMixedContextInitializers() {
MockServletContext sc = new MockServletContext("");
sc.addInitParameter(ContextLoader.CONFIG_LOCATION_PARAM,
"org/springframework/web/context/WEB-INF/ContextLoaderTests-acc-context.xml");
sc.addInitParameter(ContextLoader.CONTEXT_INITIALIZER_CLASSES_PARAM, TestContextInitializer.class.getName());
sc.addInitParameter(ContextLoader.GLOBAL_INITIALIZER_CLASSES_PARAM, TestWebContextInitializer.class.getName());
ContextLoaderListener listener = new ContextLoaderListener();
listener.contextInitialized(new ServletContextEvent(sc));
WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext(sc);
TestBean testBean = wac.getBean(TestBean.class); TestBean testBean = wac.getBean(TestBean.class);
assertThat(testBean.getName(), equalTo("testName")); assertThat(testBean.getName(), equalTo("testName"));
assertThat(wac.getServletContext().getAttribute("initialized"), notNullValue()); assertThat(wac.getServletContext().getAttribute("initialized"), notNullValue());
@ -136,7 +158,7 @@ public final class ContextLoaderTests {
@Test @Test
public void testRegisteredContextInitializerCanAccessServletContextParamsViaEnvironment() { public void testRegisteredContextInitializerCanAccessServletContextParamsViaEnvironment() {
MockServletContext sc = new MockServletContext(""); MockServletContext sc = new MockServletContext("");
// config file doesn't matter. just a placeholder // config file doesn't matter - just a placeholder
sc.addInitParameter(ContextLoader.CONFIG_LOCATION_PARAM, sc.addInitParameter(ContextLoader.CONFIG_LOCATION_PARAM,
"/org/springframework/web/context/WEB-INF/empty-context.xml"); "/org/springframework/web/context/WEB-INF/empty-context.xml");
@ -153,12 +175,13 @@ public final class ContextLoaderTests {
sc.addInitParameter(ContextLoader.CONFIG_LOCATION_PARAM, sc.addInitParameter(ContextLoader.CONFIG_LOCATION_PARAM,
"/org/springframework/web/context/WEB-INF/empty-context.xml"); "/org/springframework/web/context/WEB-INF/empty-context.xml");
sc.addInitParameter(ContextLoader.CONTEXT_INITIALIZER_CLASSES_PARAM, sc.addInitParameter(ContextLoader.CONTEXT_INITIALIZER_CLASSES_PARAM,
StringUtils.arrayToCommaDelimitedString(new Object[]{UnknownContextInitializer.class.getName()})); StringUtils.arrayToCommaDelimitedString(new Object[] {UnknownContextInitializer.class.getName()}));
ContextLoaderListener listener = new ContextLoaderListener(); ContextLoaderListener listener = new ContextLoaderListener();
try { try {
listener.contextInitialized(new ServletContextEvent(sc)); listener.contextInitialized(new ServletContextEvent(sc));
fail("expected exception"); fail("expected exception");
} catch (IllegalArgumentException ex) { }
catch (IllegalArgumentException ex) {
assertTrue(ex.getMessage().contains("not assignable")); assertTrue(ex.getMessage().contains("not assignable"));
} }
} }
@ -321,7 +344,9 @@ public final class ContextLoaderTests {
} }
} }
private static class TestContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> { private static class TestContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override @Override
public void initialize(ConfigurableApplicationContext applicationContext) { public void initialize(ConfigurableApplicationContext applicationContext) {
ConfigurableEnvironment environment = applicationContext.getEnvironment(); ConfigurableEnvironment environment = applicationContext.getEnvironment();
@ -334,7 +359,9 @@ public final class ContextLoaderTests {
} }
} }
private static class TestWebContextInitializer implements ApplicationContextInitializer<ConfigurableWebApplicationContext> { private static class TestWebContextInitializer implements ApplicationContextInitializer<ConfigurableWebApplicationContext> {
@Override @Override
public void initialize(ConfigurableWebApplicationContext applicationContext) { public void initialize(ConfigurableWebApplicationContext applicationContext) {
ServletContext ctx = applicationContext.getServletContext(); // type-safe access to servlet-specific methods ServletContext ctx = applicationContext.getServletContext(); // type-safe access to servlet-specific methods
@ -342,7 +369,9 @@ public final class ContextLoaderTests {
} }
} }
private static class EnvApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableWebApplicationContext> { private static class EnvApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableWebApplicationContext> {
@Override @Override
public void initialize(ConfigurableWebApplicationContext applicationContext) { public void initialize(ConfigurableWebApplicationContext applicationContext) {
// test that ApplicationContextInitializers can access ServletContext properties // test that ApplicationContextInitializers can access ServletContext properties
@ -352,11 +381,15 @@ public final class ContextLoaderTests {
} }
} }
private static interface UnknownApplicationContext extends ConfigurableApplicationContext { private static interface UnknownApplicationContext extends ConfigurableApplicationContext {
void unheardOf(); void unheardOf();
} }
private static class UnknownContextInitializer implements ApplicationContextInitializer<UnknownApplicationContext> { private static class UnknownContextInitializer implements ApplicationContextInitializer<UnknownApplicationContext> {
@Override @Override
public void initialize(UnknownApplicationContext applicationContext) { public void initialize(UnknownApplicationContext applicationContext) {
applicationContext.unheardOf(); applicationContext.unheardOf();

Loading…
Cancel
Save