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>
再次啟動工程,問題解決。