1. 问题描述
在开发询盘功能时,遇到一个需求,就是后台定时任务执行用电施工业务的工单下发。
使用的技术是 spring quartz,因为其他应用有先例,配置quartz 完成后,先写了一个 helloworld 测试下。
然而却发现,每次到定时时间后,程序都会执行两次。
2. 分析过程
先使用 bing 搜索了下看别人是否也遇到过类似问题,果然有。
http://blog.csdn.net/jiang117/article/details/43077275
上面文档的作者,查找的原因是 ContextLoaderListener 和 DispatcherServlet 对应用上下文重复加载,导致问题出现。
给出的解决方法如下:
带着这个疑惑,我检查一下自己项目的 web.xml 文件,发现果然有问题。
下面是 ContextLoaderListener 中加载的上下文。
<!-- 加载Spring和Mybatis的配置信息 --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/classes/spring/spring-all.xml</param-value> </context-param> <!-- Spring监听器 --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>
这是 DispatcherServlet 加载的上下文
<!-- Spring MVC servlet --> <servlet> <servlet-name>SpringMVC</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/classes/spring/spring-all.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> <async-supported>true</async-supported> </servlet> <servlet-mapping> <servlet-name>SpringMVC</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
经过对比发现,两个上下文都会加载 /WEB-INF/classes/spring/spring-all.xml, spring-all.xml 文件则包含了所有的 spring 配置文件,也就是所有的上下文配置。
这样就会产生一个问题,就是 spring-all .xml 上下文中所有的配置都会被实例化两次,因此也就会导致该问题出现。
3. 解决过程
找到了问题原因,下一步就要修改 web.xml 中的配置,解决 spring-all xml 上下文被实例化两次的问题。
解决问题之前,先要弄清楚 DispatcherServlet 和 ContextLoaderListener 这两个应用上下文之间的关系。
下面的内容选自 《spring 实战 第4版》 p139
两个应用上下文之间的故事 当 DispatcherServlet 启动的时候,它会创建 Spring 上下文,并加载配置文件或者配置类中所声明的 bean。 同时在 Spring Web 应用中,通常还有一个另外的应用上下文,它由 ContextLoaderListener 创建。 两者的分工有所不同, DispatcherServlet 中加载 Web 组件的 bean,如 Controller,viewResolver 以及处理器映射。而ContextLoaderListener 要加载应用中其他的 bean,这些 bean 通常是驱动应用后端的中间层和数据层组件。
同时在如下博客链接 http://www.cnblogs.com/weknow619/p/6341395.html 得到说明:
因此决定使用 ContextLoaderListener 加载所有配置,而将 DispatchServlet 上下文去除。
<context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/classes/spring/spring-all.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- Spring MVC servlet --> <servlet> <servlet-name>SpringMVC</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value></param-value> </init-param> <load-on-startup>1</load-on-startup> <async-supported>true</async-supported> </servlet> <servlet-mapping> <servlet-name>SpringMVC</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
再次启动工程,问题解决。