在给新同事培训Spring MVC时,有人问:可以不配置ContextLoaderListener吗
所谓ContextLoaderListener,就是在web部署描述符即web.xml里面经常配置的一个监听器,如下
org.springframework.web.context.ContextLoaderListener
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方法
我们看下类图结构,只列出一部分属性和方法
因为 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
注意:这种情况下,你的应用程序是无法使用WebApplicationContext的
正常情况下,都会配置ContextLoaderListener,因为我们知道Spring IOC的两种实现
基础的就是BeanFactory,高级的就是ApplicationContext,除非在资源非常有限的情况下,才使用BeanFactory
否则都使用ApplicationContext,而WebApplicationContext就是其中的一种高级实现,它能提供很多有用的方法
那么在应用程序如何获取 WebApplicationContext 呢,有多种方式,最简单的就是
WebApplicationContext wac = ContextLoader.getCurrentWebApplicationContext();
还有基于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