关于ContextLoaderListener那点事

在给新同事培训Spring MVC时,有人问:可以不配置ContextLoaderListener吗

所谓ContextLoaderListener,就是在web部署描述符即web.xml里面经常配置的一个监听器,如下

	
		org.springframework.web.context.ContextLoaderListener
	

那么配合它一起使用的,经常是context-param,用来指定Spring要加载的配置文件,比如

	
		contextConfigLocation
		/WEB-INF/teach-servlet.xml
	

	
	
		teach
		org.springframework.web.servlet.DispatcherServlet
		1
	
	
		teach
		*.action
	

上面两段就是在使用Spring MVC时,常用的配置,DispatcherServlet作为Spring MVC控制器的核心调度器

至于 teach-servlet.xml 就是配置一些Spring MVC需要使用的视图解析器等等


那么问题是:listener节点可以不配置吗?答案是肯定的:可以不做任何配置!

查看了ContextLoaderListener源代码,发现它继承自ContextLoader,并且实现ServletContextListener接口

肯定得实现这个接口了,不然怎么作为Servlet的监听器呢。。。

ContextLoaderListener 源代码很简单,核心是实现了 ServletContextListener 的contextInitialized和contextDestroyed方法

我们看下类图结构,只列出一部分属性和方法

关于ContextLoaderListener那点事_第1张图片


因为 contextInitialized和contextDestroyed 方法分别调用了 ContextLoader里面的initWebApplicationContext和closeWebApplicationContext方法

所以核心最终还是 ContextLoader 实现了这个监听器,那这个监听器实现了什么功能呢,我们发现有两个重要属性

contextConfigLocation:即在web.xml里面指定的配置文件所在目录,如果不指定,Spring 会加载WEB_INF目录下,符合 *Context.xml 或 spring*.xml 规则的文件

currentContextPerThread:保存了当前WebApplicationContext


其实监听器的加载过程可以描述为:

先判WebApplicationContext是否已存在,不存在的话则初始化一个XmlWebApplicationContext(WebApplicationContext的子类),并把该实例put到 currentContextPerThread 中。而初始化 XmlWebApplicationContext 时,就跟我们使用 new ClassPathXmlApplicationContext(contextConfigLocation)一样

将我们配置的各种bean都添加到XmlWebApplicationContext中,所以我们知道 ApplicationContext 提供各种 getBean的方法。。。

并且可以发现 ContextLoader还提供了获取当前 WebApplicationContext的静态方法:之所以能获取,是因为initWebApplicationContext初始化方法把创建的XmlWebApplicationContext 塞到了 currentContextPerThread 中


说了一堆,跟Spring MVC 不配置ContextLoaderListener有什么关系呢。。。

因为 ContextLoaderListener 本质上是创建了一个 WebApplicationContext ,所以你的项目里面,如果不使用 WebApplicationContext 就可以不配置该节点。

那么只要做这种配置也是可以的:


	
		teach
		org.springframework.web.servlet.DispatcherServlet
		
			contextConfigLocation
			/WEB-INF/teach-servlet.xml
		
		1
	
	
		teach
		*.action
	

发现Spring MVC 所需的配置文件不使用context-param节点指定,直接在DispatcherServlet里面配置即可

注意:这种情况下,你的应用程序是无法使用WebApplicationContext的


正常情况下,都会配置ContextLoaderListener,因为我们知道Spring IOC的两种实现

基础的就是BeanFactory,高级的就是ApplicationContext,除非在资源非常有限的情况下,才使用BeanFactory

否则都使用ApplicationContext,而WebApplicationContext就是其中的一种高级实现,它能提供很多有用的方法


那么在应用程序如何获取 WebApplicationContext 呢,有多种方式,最简单的就是

WebApplicationContext wac = ContextLoader.getCurrentWebApplicationContext();

这个很熟悉了吧,刚才提到了,当前应用的WebApplicationContext就保存在 ContextLoader的currentContextPerThread属性当中


还有基于ServletContext上下文获取的方式

        ServletContext sc = request.getSession().getServletContext();
        ApplicationContext ac1 = WebApplicationContextUtils.getRequiredWebApplicationContext(sc);
        ApplicationContext ac2 = WebApplicationContextUtils.getWebApplicationContext(sc);
        WebApplicationContext wac1 = (WebApplicationContext) sc.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);


还有一些更合适的,基于Spring提供的抽象类或者接口,在初始化Bean时注入ApplicationContext

继承自抽象类ApplicationObjectSupport
说明:抽象类ApplicationObjectSupport提供getApplicationContext()方法,可以方便的获取到ApplicationContext。
Spring初始化时,会通过该抽象类的setApplicationContext(ApplicationContext context)方法将ApplicationContext 对象注入。

继承自抽象类WebApplicationObjectSupport
说明:类似上面方法,调用getWebApplicationContext()获取WebApplicationContext

实现接口ApplicationContextAware
说明:实现该接口的setApplicationContext(ApplicationContext context)方法,并保存ApplicationContext 对象。
Spring初始化时,会通过该方法将ApplicationContext对象注入。

你可能感兴趣的:(Spring)