您的位置:首页 > 移动开发

ContextLoader中setContextInitializers(ApplicationContextInitializer<?>... initializers)的思考

2016-06-22 17:12 519 查看
最近查看了ContextLoader的源代码,涉及到了contextInitializerClasses参数,由于自己的项目没有用到该参数,对此产生了好奇,参考网上资料,整理一下:

ContextLoader中部分代码:

public class ContextLoader {
public static final String CONTEXT_ID_PARAM = "contextId";
public static final String CONFIG_LOCATION_PARAM = "contextConfigLocation";
public static final String CONTEXT_CLASS_PARAM = "contextClass";
public static final String CONTEXT_INITIALIZER_CLASSES_PARAM = "contextInitializerClasses";
public static final String GLOBAL_INITIALIZER_CLASSES_PARAM = "globalInitializerClasses";
public static final String LOCATOR_FACTORY_SELECTOR_PARAM = "locatorFactorySelector";
public static final String LOCATOR_FACTORY_KEY_PARAM = "parentContextKey";
private static final String INIT_PARAM_DELIMITERS = ",; \t\n";
private static final String DEFAULT_STRATEGIES_PATH = "ContextLoader.properties";
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
......
//初始化时调用此方法
configureAndRefreshWebApplicationContext(cwac, servletContext);
......
}

protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
......
//使用contextConfigLocation参数配置
String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
if (configLocationParam != null) {
wac.setConfigLocation(configLocationParam);
}
......
//ContextLoader创建ConfigurableWebApplicationContext
customizeContext(sc, wac);
wac.refresh();
}

protected void customizeContext(ServletContext sc, ConfigurableWebApplicationContext wac) {
//处理ApplicationContextInitializer的地方
List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>> initializerClasses
= determineContextInitializerClasses(sc);
for (Class<ApplicationContextInitializer<ConfigurableApplicationContext>> initializerClass : initializerClasses) {
......
this.contextInitializers.add(BeanUtils.instantiateClass(initializerClass));
}
AnnotationAwareOrderComparator.sort(this.contextInitializers);
for (ApplicationContextInitializer<ConfigurableApplicationContext> initializer : this.contextInitializers) {
initializer.initialize(wac);
}
}

//返回由globalInitializerClasses和contextInitializerClasses定义的ApplicationContextInitializer列表
protected List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>>
determineContextInitializerClasses(ServletContext servletContext) {
List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>> classes =
new ArrayList<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>>();

//globalInitializerClasses
String globalClassNames = servletContext.getInitParameter(GLOBAL_INITIALIZER_CLASSES_PARAM);
if (globalClassNames != null) {
for (String className : StringUtils.tokenizeToStringArray(globalClassNames, INIT_PARAM_DELIMITERS)) {
classes.add(loadInitializerClass(className));
}
}

//contextInitializerClasses
String localClassNames = servletContext.getInitParameter(CONTEXT_INITIALIZER_CLASSES_PARAM);
if (localClassNames != null) {
for (String className : StringUtils.tokenizeToStringArray(localClassNames, INIT_PARAM_DELIMITERS)) {
classes.add(loadInitializerClass(className));
}
}
return classes;
}
@SuppressWarnings("unchecked")
private Class<ApplicationContextInitializer<ConfigurableApplicationContext>> loadInitializerClass(String className) {
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);
}
}
}
涉及到的ApplicationContextInitializer类:

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} interface has been
* implemented or if the @{@link org.springframework.core.annotation.Order Order}
* annotation is present and to sort instances accordingly if so prior to invocation.
*
* 回调接口,用于ConfigurableApplicationContext.refresh()刷新前,初始化
* Spring的ConfigurableApplicationContext。
*
* 通常用在web应用程序中需要对应用程序上下文做一些程序初始化时。例如:注册property资源文件或者
* 激活对上下文环境的环境配置文件。分别参见ContextLoader和FrameworkServlet,分别通过在<context-param>
* 和<init-param>中声明contextInitializerClasses参数来的支持。
*
* ApplicationContextInitializer处理器检测是否实现了Ordered接口或者是否有@Order注解的实例存在,以此
* 按声明的顺序进行调用。
*
* @author Chris Beams
* @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#applyInitializers
*/
public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {
/**
* 初始化给定的应用程序上下文。
* @param applicationContext the application to configure
*/
void initialize(C applicationContext);
}
由此可知,可以通过ApplicationContextInitializer更改ConfigurableApplicationContext子类的行为。

举个例子:

XmlWebApplicationContext.setAllowBeanDefinitionOverriding()其默认值为true。如果把此值设置为false,怎么解决?

有两个思路:

1、写org.springframework.web.context.ContextLoaderListener和org.springframework.web.servlet.DispatcherServlet的子类:

在相应的方法中进行设置context.setAllowBeanDefinitionOverriding(false); 

这种方法如果处理不好,会引起很多麻烦,不推荐。

2、根据ApplicationContextInitializer接口的说明,通过参数配置来改变applicationContext的行为:

1)首先定义一个ApplicationContextInitializer的子类:

public class MyApplicationContextInitializer implements ApplicationContextInitializer<XmlWebApplicationContext> {
public void initialize(XmlWebApplicationContext applicationContext) {
//XmlWebApplicationContext是ConfigurableApplicationContext的子类
//由此可以改变applicationContext的默认行为。
applicationContext.setAllowBeanDefinitionOverriding(false);
}
}


2)在配置文件中引用
由ContextLoaderListener调用的配置方式:

<context-param>
<param-name>contextClass</param-name>
<param-value>org.springframework.web.context.support.XmlWebApplicationContext</param-value>
</context-param>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:applicationContext.xml</param-value>
</context-param>
<context-param>
<param-name>contextInitializerClasses</param-name>
<param-value>MyApplicationContextInitializer</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

由DispatcherServlet调用的配置方式

<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-servlet.xml</param-value>
</init-param>
<init-param> 
<param-name>contextInitializerClasses</param-name>
<param-value>MyApplicationContextInitializer</param-value>
</init-param>
</servlet>
通过这种方式,可以改变XmlWebApplicationContext其他的属性,如果有必要的话。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息