使用spring quartz的时候出现了一次触发执行两次的问题,上网搜索后发现原因是 ContextLoaderListener 和 DispatcherServlet 对应用上下文重复加载,导致问题出现。
下面是 ContextLoaderListener 中加载的上下文:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/*.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
这是 DispatcherServlet 加载的上下文:
<!-- springMVC配置 -->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/spring.xml</param-value>
</init-param>
<load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
经过对比发现,两个上下文都会加载 /WEB-INF/classes/spring/spring.xml, spring.xml 文件则包含了所有的 spring 配置文件,也就是所有的上下文配置。
这样就会产生一个问题,就是 spring.xml 上下文中所有的配置都会被实例化两次,因此也就会导致该问题出现。
DispatcherServlet 和 ContextLoaderListener 这两个应用上下文之间的关系:
当 DispatcherServlet 启动的时候,它会创建 Spring 上下文,并加载配置文件或者配置类中所声明的 bean。
同时在 Spring Web 应用中,通常还有一个另外的应用上下文,它由 ContextLoaderListener 创建。
两者的分工有所不同, DispatcherServlet 中加载 Web 组件的 bean,如 Controller,viewResolver 以及处理器映射。
而ContextLoaderListener 要加载应用中其他的 bean,这些 bean 通常是驱动应用后端的中间层和数据层组件。
范围而言:DispatcherServlet可以引用由ContextLoaderListener创建的ApplicationContext,反过来却不行,所以解决方案是使用 ContextLoaderListener 加载所有配置,而将 DispatchServlet 上下文去除
<!-- springMVC配置 -->
<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>2</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
再次启动工程,问题解决。