Skip to content

Commit 5fb2665

Browse files
committed
Introduced "globalInitializerClasses" next to the existing "contextInitializerClasses", applying to FrameworkServlets as well
Issue: SPR-11314 (cherry picked from commit 91881ff)
1 parent 52c11ea commit 5fb2665

File tree

4 files changed

+320
-183
lines changed

4 files changed

+320
-183
lines changed

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

+110-83
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818

1919
import java.io.IOException;
2020
import java.util.ArrayList;
21-
import java.util.Collections;
2221
import java.util.List;
2322
import java.util.Map;
2423
import java.util.Properties;
@@ -78,8 +77,8 @@
7877
*
7978
* <p>As of Spring 3.1, {@code ContextLoader} supports injecting the root web
8079
* application context via the {@link #ContextLoader(WebApplicationContext)}
81-
* constructor, allowing for programmatic configuration in Servlet 3.0+ environments. See
82-
* {@link org.springframework.web.WebApplicationInitializer} for usage examples.
80+
* constructor, allowing for programmatic configuration in Servlet 3.0+ environments.
81+
* See {@link org.springframework.web.WebApplicationInitializer} for usage examples.
8382
*
8483
* @author Juergen Hoeller
8584
* @author Colin Sampaleanu
@@ -91,6 +90,20 @@
9190
*/
9291
public class ContextLoader {
9392

93+
/**
94+
* Config param for the root WebApplicationContext id,
95+
* to be used as serialization id for the underlying BeanFactory: {@value}
96+
*/
97+
public static final String CONTEXT_ID_PARAM = "contextId";
98+
99+
/**
100+
* Name of servlet context parameter (i.e., {@value}) that can specify the
101+
* config location for the root context, falling back to the implementation's
102+
* default otherwise.
103+
* @see org.springframework.web.context.support.XmlWebApplicationContext#DEFAULT_CONFIG_LOCATION
104+
*/
105+
public static final String CONFIG_LOCATION_PARAM = "contextConfigLocation";
106+
94107
/**
95108
* Config param for the root WebApplicationContext implementation class to use: {@value}
96109
* @see #determineContextClass(ServletContext)
@@ -99,25 +112,18 @@ public class ContextLoader {
99112
public static final String CONTEXT_CLASS_PARAM = "contextClass";
100113

101114
/**
102-
* Config param for the root WebApplicationContext id,
103-
* to be used as serialization id for the underlying BeanFactory: {@value}
104-
*/
105-
public static final String CONTEXT_ID_PARAM = "contextId";
106-
107-
/**
108-
* Config param for which {@link ApplicationContextInitializer} classes to use
109-
* for initializing the web application context: {@value}
115+
* Config param for {@link ApplicationContextInitializer} classes to use
116+
* for initializing the root web application context: {@value}
110117
* @see #customizeContext(ServletContext, ConfigurableWebApplicationContext)
111118
*/
112119
public static final String CONTEXT_INITIALIZER_CLASSES_PARAM = "contextInitializerClasses";
113120

114121
/**
115-
* Name of servlet context parameter (i.e., {@value}) that can specify the
116-
* config location for the root context, falling back to the implementation's
117-
* default otherwise.
118-
* @see org.springframework.web.context.support.XmlWebApplicationContext#DEFAULT_CONFIG_LOCATION
122+
* Config param for global {@link ApplicationContextInitializer} classes to use
123+
* for initializing all web application contexts in the current application: {@value}
124+
* @see #customizeContext(ServletContext, ConfigurableWebApplicationContext)
119125
*/
120-
public static final String CONFIG_LOCATION_PARAM = "contextConfigLocation";
126+
public static final String GLOBAL_INITIALIZER_CLASSES_PARAM = "globalInitializerClasses";
121127

122128
/**
123129
* Optional servlet context parameter (i.e., "{@code locatorFactorySelector}")
@@ -147,6 +153,12 @@ public class ContextLoader {
147153
*/
148154
public static final String LOCATOR_FACTORY_KEY_PARAM = "parentContextKey";
149155

156+
/**
157+
* Any number of these characters are considered delimiters between
158+
* multiple values in a single init-param String value.
159+
*/
160+
private static final String INIT_PARAM_DELIMITERS = ",; \t\n";
161+
150162
/**
151163
* Name of the class path resource (relative to the ContextLoader class)
152164
* that defines ContextLoader's default strategy names.
@@ -381,14 +393,71 @@ protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicati
381393
}
382394

383395
wac.setServletContext(sc);
384-
String initParameter = sc.getInitParameter(CONFIG_LOCATION_PARAM);
385-
if (initParameter != null) {
386-
wac.setConfigLocation(initParameter);
396+
String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
397+
if (configLocationParam != null) {
398+
wac.setConfigLocation(configLocationParam);
387399
}
400+
401+
// The wac environment's #initPropertySources will be called in any case when the context
402+
// is refreshed; do it eagerly here to ensure servlet property sources are in place for
403+
// use in any post-processing or initialization that occurs below prior to #refresh
404+
ConfigurableEnvironment env = wac.getEnvironment();
405+
if (env instanceof ConfigurableWebEnvironment) {
406+
((ConfigurableWebEnvironment) env).initPropertySources(sc, null);
407+
}
408+
388409
customizeContext(sc, wac);
389410
wac.refresh();
390411
}
391412

413+
/**
414+
* Customize the {@link ConfigurableWebApplicationContext} created by this
415+
* ContextLoader after config locations have been supplied to the context
416+
* but before the context is <em>refreshed</em>.
417+
* <p>The default implementation {@linkplain #determineContextInitializerClasses(ServletContext)
418+
* determines} what (if any) context initializer classes have been specified through
419+
* {@linkplain #CONTEXT_INITIALIZER_CLASSES_PARAM context init parameters} and
420+
* {@linkplain ApplicationContextInitializer#initialize invokes each} with the
421+
* given web application context.
422+
* <p>Any {@code ApplicationContextInitializers} implementing
423+
* {@link org.springframework.core.Ordered Ordered} or marked with @{@link
424+
* org.springframework.core.annotation.Order Order} will be sorted appropriately.
425+
* @param sc the current servlet context
426+
* @param wac the newly created application context
427+
* @see #createWebApplicationContext(ServletContext, ApplicationContext)
428+
* @see #CONTEXT_INITIALIZER_CLASSES_PARAM
429+
* @see ApplicationContextInitializer#initialize(ConfigurableApplicationContext)
430+
*/
431+
protected void customizeContext(ServletContext sc, ConfigurableWebApplicationContext wac) {
432+
List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>> initializerClasses =
433+
determineContextInitializerClasses(sc);
434+
if (initializerClasses.isEmpty()) {
435+
// no ApplicationContextInitializers have been declared -> nothing to do
436+
return;
437+
}
438+
439+
ArrayList<ApplicationContextInitializer<ConfigurableApplicationContext>> initializerInstances =
440+
new ArrayList<ApplicationContextInitializer<ConfigurableApplicationContext>>();
441+
442+
for (Class<ApplicationContextInitializer<ConfigurableApplicationContext>> initializerClass : initializerClasses) {
443+
Class<?> initializerContextClass =
444+
GenericTypeResolver.resolveTypeArgument(initializerClass, ApplicationContextInitializer.class);
445+
if (initializerContextClass != null) {
446+
Assert.isAssignable(initializerContextClass, wac.getClass(), String.format(
447+
"Could not add context initializer [%s] since its generic parameter [%s] " +
448+
"is not assignable from the type of application context used by this " +
449+
"context loader [%s]: ", initializerClass.getName(), initializerContextClass.getName(),
450+
wac.getClass().getName()));
451+
}
452+
initializerInstances.add(BeanUtils.instantiateClass(initializerClass));
453+
}
454+
455+
AnnotationAwareOrderComparator.sort(initializerInstances);
456+
for (ApplicationContextInitializer<ConfigurableApplicationContext> initializer : initializerInstances) {
457+
initializer.initialize(wac);
458+
}
459+
}
460+
392461
/**
393462
* Return the WebApplicationContext implementation class to use, either the
394463
* default XmlWebApplicationContext or a custom context class if specified.
@@ -426,80 +495,38 @@ protected Class<?> determineContextClass(ServletContext servletContext) {
426495
* @param servletContext current servlet context
427496
* @see #CONTEXT_INITIALIZER_CLASSES_PARAM
428497
*/
429-
@SuppressWarnings("unchecked")
430498
protected List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>>
431499
determineContextInitializerClasses(ServletContext servletContext) {
432-
String classNames = servletContext.getInitParameter(CONTEXT_INITIALIZER_CLASSES_PARAM);
500+
433501
List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>> classes =
434-
new ArrayList<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>>();
435-
if (classNames != null) {
436-
for (String className : StringUtils.tokenizeToStringArray(classNames, ",")) {
437-
try {
438-
Class<?> clazz = ClassUtils.forName(className, ClassUtils.getDefaultClassLoader());
439-
Assert.isAssignable(ApplicationContextInitializer.class, clazz,
440-
"class [" + className + "] must implement ApplicationContextInitializer");
441-
classes.add((Class<ApplicationContextInitializer<ConfigurableApplicationContext>>)clazz);
442-
}
443-
catch (ClassNotFoundException ex) {
444-
throw new ApplicationContextException(
445-
"Failed to load context initializer class [" + className + "]", ex);
446-
}
447-
}
448-
}
449-
return classes;
450-
}
502+
new ArrayList<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>>();
451503

452-
/**
453-
* Customize the {@link ConfigurableWebApplicationContext} created by this
454-
* ContextLoader after config locations have been supplied to the context
455-
* but before the context is <em>refreshed</em>.
456-
* <p>The default implementation {@linkplain #determineContextInitializerClasses(ServletContext)
457-
* determines} what (if any) context initializer classes have been specified through
458-
* {@linkplain #CONTEXT_INITIALIZER_CLASSES_PARAM context init parameters} and
459-
* {@linkplain ApplicationContextInitializer#initialize invokes each} with the
460-
* given web application context.
461-
* <p>Any {@code ApplicationContextInitializers} implementing
462-
* {@link org.springframework.core.Ordered Ordered} or marked with @{@link
463-
* org.springframework.core.annotation.Order Order} will be sorted appropriately.
464-
* @param servletContext the current servlet context
465-
* @param applicationContext the newly created application context
466-
* @see #createWebApplicationContext(ServletContext, ApplicationContext)
467-
* @see #CONTEXT_INITIALIZER_CLASSES_PARAM
468-
* @see ApplicationContextInitializer#initialize(ConfigurableApplicationContext)
469-
*/
470-
protected void customizeContext(ServletContext servletContext, ConfigurableWebApplicationContext applicationContext) {
471-
List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>> initializerClasses =
472-
determineContextInitializerClasses(servletContext);
473-
if (initializerClasses.size() == 0) {
474-
// no ApplicationContextInitializers have been declared -> nothing to do
475-
return;
504+
String globalClassNames = servletContext.getInitParameter(GLOBAL_INITIALIZER_CLASSES_PARAM);
505+
if (globalClassNames != null) {
506+
for (String className : StringUtils.tokenizeToStringArray(globalClassNames, INIT_PARAM_DELIMITERS)) {
507+
classes.add(loadInitializerClass(className));
508+
}
476509
}
477510

478-
Class<?> contextClass = applicationContext.getClass();
479-
ArrayList<ApplicationContextInitializer<ConfigurableApplicationContext>> initializerInstances =
480-
new ArrayList<ApplicationContextInitializer<ConfigurableApplicationContext>>();
481-
482-
for (Class<ApplicationContextInitializer<ConfigurableApplicationContext>> initializerClass : initializerClasses) {
483-
Class<?> initializerContextClass =
484-
GenericTypeResolver.resolveTypeArgument(initializerClass, ApplicationContextInitializer.class);
485-
if (initializerContextClass != null) {
486-
Assert.isAssignable(initializerContextClass, contextClass, String.format(
487-
"Could not add context initializer [%s] as its generic parameter [%s] " +
488-
"is not assignable from the type of application context used by this " +
489-
"context loader [%s]: ", initializerClass.getName(), initializerContextClass.getName(),
490-
contextClass.getName()));
511+
String localClassNames = servletContext.getInitParameter(CONTEXT_INITIALIZER_CLASSES_PARAM);
512+
if (localClassNames != null) {
513+
for (String className : StringUtils.tokenizeToStringArray(localClassNames, INIT_PARAM_DELIMITERS)) {
514+
classes.add(loadInitializerClass(className));
491515
}
492-
initializerInstances.add(BeanUtils.instantiateClass(initializerClass));
493516
}
494517

495-
ConfigurableEnvironment env = applicationContext.getEnvironment();
496-
if (env instanceof ConfigurableWebEnvironment) {
497-
((ConfigurableWebEnvironment)env).initPropertySources(servletContext, null);
498-
}
518+
return classes;
519+
}
499520

500-
Collections.sort(initializerInstances, new AnnotationAwareOrderComparator());
501-
for (ApplicationContextInitializer<ConfigurableApplicationContext> initializer : initializerInstances) {
502-
initializer.initialize(applicationContext);
521+
@SuppressWarnings("unchecked")
522+
private Class<ApplicationContextInitializer<ConfigurableApplicationContext>> loadInitializerClass(String className) {
523+
try {
524+
Class<?> clazz = ClassUtils.forName(className, ClassUtils.getDefaultClassLoader());
525+
Assert.isAssignable(ApplicationContextInitializer.class, clazz);
526+
return (Class<ApplicationContextInitializer<ConfigurableApplicationContext>>) clazz;
527+
}
528+
catch (ClassNotFoundException ex) {
529+
throw new ApplicationContextException("Failed to load context initializer class [" + className + "]", ex);
503530
}
504531
}
505532

0 commit comments

Comments
 (0)