Spring 定时任务重复执行的问题分析

背景:使用quartz时客户现场不知道为什么跑着跑着就停了,后来决定换成spring定时任务。

当使用spring定时任务时莫名奇妙的就是同一时间重复执行多次任务(通常情况下执行两次,有时候会达到3次)。下面记录过程并逐一分析说明,供以后参考;

1、分析原因后发现是由ClassPathXmlApplicationContext导致

通过n多次试验发现:

当任务方法中不含任何业务逻辑时(如:只打印一些标志性的信息),任务能够正常执行,没有重复执行的现象;

当任务方法中包含业务逻辑,或应用程序执行别的请求任务时,均可能导致重复执行;

最终定位到了,只要执行ServletUtil.getBean("xxxxxxxx");程序获取对象,就会导致重复执行。

1.1 在执行任务的程序中,使用到的对象加载方式如下:

ColumnService columnService = (ColumnService) ServletUtil.getBean("columnServiceImpl");

1.2 ServletUtil 的getBean 方法如下:

public static Object getBean(String beanId)

{

if(factory == null)

factory = new ClassPathXmlApplicationContext("applicationContext.xml");

Object  o = null ;

try{

o = factory.getBean(beanId);

}catch(Exception e){

System.out.println("获取bean失败");

}

return o;

}

分析发现,在Tomcat容器启动时会扫描bean初始化加载spring上下文环境;然而当显式的这样调用的时候,会再次加载bean,初始化spring上下文环境。相当于容器里除了刚启动时的一套之外又产生了一条spring上下文,当然定时任务也有了两套。

这里还说明另一个问题,单实例的bean并不是“单例模式”,只是保证一套spring上下文里只有一个bean实例。

2、应对策略

总算是找到原因了,于是考虑换一个对象获取方式,在伟大的网络中找到了答案,使用 ApplicationContextAware 接口可有效避免此问题。

2.1 重写bean获取工具类:

public class SpringContextUtil implements ApplicationContextAware {

// Spring应用上下文环境

private static ApplicationContext applicationContext;

/**

* 实现ApplicationContextAware接口的回调方法,设置上下文环境

*

* @param applicationContext

*/

public void setApplicationContext(ApplicationContext applicationContext) {

SpringContextUtil.applicationContext = applicationContext;

}

/**

* @return ApplicationContext

*/

public static ApplicationContext getApplicationContext() {

return applicationContext;

}

/**

* 获取对象

* 这里重写了bean方法,起主要作用

* @param name

* @return Object 一个以所给名字注册的bean的实例

* @throws BeansException

*/

public static Object getBean(String name) throws BeansException {

return applicationContext.getBean(name);

}

}

分析:加载Spring配置文件时,如果Spring配置文件中所定义的Bean类实现了ApplicationContextAware 接口,那么在加载Spring配置文件时,会自动调用ApplicationContextAware 接口中的

public void setApplicationContext(ApplicationContext context) throws BeansException 方法,获得ApplicationContext对象。 前提必须在Spring配置文件中指定该类

2.2 配置spring 文件 applicationContext.xml,如下

<bean id="SpringContextUtil" class="com.carnation.utils.SpringContextUtil" lazy-init="false"></bean>

如上配置一定要注意当spring配置default-lazy-init="true" 时,此类的配置一定要指定 lazy-init="false",否则无法通过此类获取到bean(会报null异常)

2.3 改变bean的获取方式

将原始使用 ServletUtil.getBean("xxxxxxxx"); 获取bean的全部修改为 SpringContextUtil.getBean("xxxxxxxx"); 如下:

UserService userService= (UserService)SpringContextUtil.getBean("userServiceImpl");

DepartmentService departmentService = (DepartmentService)SpringContextUtil.getBean("departmentServiceImpl");

UserRoleService userRoleService = (UserRoleService)SpringContextUtil.getBean("userRoleServiceImpl");

SyncRoService syncRoService = (SyncRoService) SpringContextUtil.getBean("syncRoServiceImpl");

到此,定时任务重复执行的问题得到解决(这里只分析了我在实际的场景下遇到的问题,但并不一定适合所有)

其它参考网址 http://zoroeye.iteye.com/blog/2186822

时间: 2024-08-23 02:40:43

Spring 定时任务重复执行的问题分析的相关文章

集群环境下如何防止定时任务重复执行?

起因 最近做项目是遇到这样一个问题:系统需要在每天的16:00向一些符合特定条件的用户发送一份邮件,发送成功后修改掉数据库中对应数据的标志位.本来是没有问题的,但后来系统被部署到了集群环境下,导致每天会向这些用户发送多次同样的数据,遭到了客户的抱怨. 解决 下面来介绍一下处理这种问题的解决办法: 1.在数据库中建立tm_job_group表 Name Type Comments group_id number 组id interval number 时间间隔区分定时任务的间隔即多长时间内不可重复

预发环境与生产环境共享数据库时定时任务重复执行问题解决

背景: 为保证预发环境的真实性,预发与生产环境往往共享数据库,在定时任务列表中,预发与生产环境都会从任务列表中获取定时任务,然后执行,这会导致定时任务会执行重复. 解决方法: 在job中增加一个环境变量字段,如test,stg,prod等,当创建任务的时候获取执行创建任务服务器的profile,根据profile插入到jod的上述字段中.定时任务执行时判断任务是否符合执行机器的profile,符合则执行,不符合则不执行. 具体可执行操作: 1.在tomcat或者启动脚本中加入vm参数,例如 -D

Spring 定时任务实现 以及无法正常执行分析

背景:使用quartz时客户现场不知道为什么跑着跑着就停了,后来决定换成spring定时任务. 按道理使用spring 定时任务应该是很简单的,不过还是踩到一些坑,下面记录过程并逐一分析说明,供以后参考: 1.使用注解的形式完成执行定时任务 1.1 applicationContext.xml中要包含对类扫描声明,以及开启task定时任务注解声明(如下) <context:component-scan base-package="com.carnation" /> <

使用轻量级Spring @Scheduled注解执行定时任务

WEB项目中需要加入一个定时执行任务,可以使用Quartz来实现,由于项目就一个定时任务,所以想简单点,不用去配置那些Quartz的配置文件,所以就采用了Spring @Scheduled注解来实现了定时任务.在这里做个备注. spring配置文件  xmlns中加入一段: xmlns:task="http://www.springframework.org/schema/task" 然后xsi:schemaLocation多加下面的内容: http://www.springframe

Spring的quartz定时器重复执行二次的问题解决

Spring的quartz定时器同一时刻重复执行二次的问题解决 最近用Spring的quartz定时器的时候,发现到时间后,任务总是重复执行两次,在tomcat或jboss下都如此. 打印出他们的hashcode,发现是不一样的,也就是说,在web容器启动的时候,重复启了两个quartz线程. 研究下来发现quartz确实会加载两次: 第一次:web容器启动的时候,读取applicationContext.xml文件时,会加载一次. 第二次:Spring本身会加载applicationConte

spring定时任务执行两次的原因与解决方法

spring定时任务,本地执行一次,放到服务器上后,每次执行时会执行两次,原因及解决办法. http://blog.csdn.net/yaobengen/article/details/70312663 spring定时任务执行两次的原因与解决方法

cron-您的定时任务真的执行了吗

今日上午,生产环境怀疑某个cron定时任务没有执行,所以需要分析日志,详细过程如下,记录只为以后工作更加效率,笔者小白,请大神指点. 普及知识: cron是一种机制,crontab是指令        它可以让系统在指定的时间,去执行某个指定的工作,我们可以使用crontab指令来管理cron机制  1.取一段时间日志 语法:sed -n '/开始时间/,/结束时间/p' filename sed -n '/Sep 20 08:50:*/,/Sep 20 09:50:*/p' /var/log/

Spring定时任务的几种实现

Spring定时任务的几种实现: 近日项目开发中需要执行一些定时任务,比如需要在每天凌晨时候,分析一次前一天的日志信息,借此机会整理了一下定时任务的几种实现方式,由于项目采用spring框架,所以我都将结合 spring框架来介绍. 一.分类 从实现的技术上来分类,目前主要有三种技术(或者说有三种产品): Java自带的java.util.Timer类,这个类允许你调度一个java.util.TimerTask任务.使用这种方式可以让你的程序按照某一个频度执行,也能在指定时间运行.一般用的较少,

(转)Spring定时任务的几种实现

Spring定时任务的几种实现 转自http://gong1208.iteye.com/blog/1773177 近日项目开发中需要执行一些定时任务,比如需要在每天凌晨时候,分析一次前一天的日志信息,借此机会整理了一下定时任务的几种实现方式,由于项目采用spring框架,所以我都将结合 spring框架来介绍. 一.分类 从实现的技术上来分类,目前主要有三种技术(或者说有三种产品): Java自带的java.util.Timer类,这个类允许你调度一个java.util.TimerTask任务.