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

最近查看了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其他的属性,如果有必要的话。



  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值