博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Spring quartz定时器动态多任务实现
阅读量:7225 次
发布时间:2019-06-29

本文共 12920 字,大约阅读时间需要 43 分钟。

hot3.png

 项目中经常会碰到需要定时执行的任务,并且需要执行什么任务,以及任务执行的时间都由用户自定义的需求。quartz是比较常用的定时器工具,并且在spring框架中也已经做了很好的集成,所以在以spring+hibernate+struts的主流架构中,我们可以采用quartz来做定时器任务的解决方案,下面,我们来看下如何在项目中使用quartz来做动态多任务定时器功能。

      1.简单单任务定时器的spring配置

      
          
              
                    
              
initJobTrigger
                    
              
            
          
              
                    
              
* * * * * ?
                      
          
              
                  
              
                  
 
  
   
    
   
initJobTrigger
       
   
  
  
   
    
   
* * * * * ?
    
  
   
    
   
   

说明:(1).InitJobDetail实例声明了需要执行的任务。其中targetObject说明了需要执行的方法所在的实例对象,targetMethod说明了要执行的方法,concurrent用于说明多个任务是否同步执行。

         (2).InitTrigger声明了一个触发器。jobDetail属性指明需要执行的任务,cronExpression声明了该任务在什么时候执行,该表达式跟linux下的crontab定时程序中使用的表达式是一样的,具体使用方法可以参考文后的参考资料。

         (3).SchedulerFactoryBean中可以定义多个触发器,以实现多任务。

     2.动态多任务实现

     实现方式:用户在前台自行维护任务列表和任务执行时间,后台将任务执行时间解析成对应的cronexpression后与任务列表一起保存到数据库中。在服务器运行期间添加的任务通过验证的(quartz会验证cronexpression是否合法以及对应时间是否已经过期)将直接添加一个任务以及触发器。如果服务器重启,在项目启动时读取配置文件执行一次任务初始化动作,保证通过验证的任务能在触发队列中,并在到达指定时间时能够触发执行。

     (1).在applicationContext.xml中添加如1中的配置,配置的任务只执行一次后即被禁用,initJobTrigger方法如下:

/**       * 容器启动时初始化任务       * @throws SchedulerException        * @throws ParseException        */      public void initJobTrigger() throws SchedulerException, ParseException{                      SchedulerFactory schedulerFactory = new StdSchedulerFactory();           Scheduler scheduler = schedulerFactory.getScheduler();           //获取任务列表的HQL语句           String hql = "from ReportJob r where r.enabled = ?";           List list = baseDao.selectByHql(hql, new Object[]{ReportJobConstants.TRUE_STRING});           if(null != list && list.size() > 0){               Iterator ite = list.iterator();               while(ite.hasNext()){                   //任务对象                   ReportJob rj = (ReportJob)ite.next();                   //定时表达式                   String cronExpression = rj.getCronExpression();                   //新建任务,任务组为默认的Scheduler.DEFAULT_GROUP,需要执行的任务类为ReportJobTodo.class                   JobDetail jobDetail =  new JobDetail("reportJob_" + rj.getGuId(), Scheduler.DEFAULT_GROUP,                               ReportJobTodo.class);                   //新建触发器,触发器为默认的Scheduler.DEFAULT_GROUP                   CronTrigger cronTrigger = new CronTrigger("trigger_" + rj.getGuId(), Scheduler.DEFAULT_GROUP);                   //为触发器设置定时表达式                   cronTrigger.setCronExpression(cronExpression);                   try{                   //启动新增定时器任务                    scheduler.scheduleJob(jobDetail, cronTrigger);                   }catch(SchedulerException e){                       //启动验证失败,设置任务标记为禁用                       e.printStackTrace();                       rj.setEnabled(ReportJobConstants.FALSE_STRING);                       baseDao.updateObject(rj);                   }               }           }           //初始化任务只需要执行一次,执行一次后移除初始化触发器           scheduler.unscheduleJob("InitTrigger", Scheduler.DEFAULT_GROUP);           //任务启动           scheduler.start();       }  /**  * 容器启动时初始化任务  * @throws SchedulerException   * @throws ParseException   */ public void initJobTrigger() throws SchedulerException, ParseException{    SchedulerFactory schedulerFactory = new StdSchedulerFactory();  Scheduler scheduler = schedulerFactory.getScheduler();  //获取任务列表的HQL语句  String hql = "from ReportJob r where r.enabled = ?";  List list = baseDao.selectByHql(hql, new Object[]{ReportJobConstants.TRUE_STRING});  if(null != list && list.size() > 0){   Iterator ite = list.iterator();   while(ite.hasNext()){    //任务对象    ReportJob rj = (ReportJob)ite.next();    //定时表达式    String cronExpression = rj.getCronExpression();    //新建任务,任务组为默认的Scheduler.DEFAULT_GROUP,需要执行的任务类为ReportJobTodo.class    JobDetail jobDetail =  new JobDetail("reportJob_" + rj.getGuId(), Scheduler.DEFAULT_GROUP,       ReportJobTodo.class);    //新建触发器,触发器为默认的Scheduler.DEFAULT_GROUP    CronTrigger cronTrigger = new CronTrigger("trigger_" + rj.getGuId(), Scheduler.DEFAULT_GROUP);    //为触发器设置定时表达式    cronTrigger.setCronExpression(cronExpression);    try{    //启动新增定时器任务     scheduler.scheduleJob(jobDetail, cronTrigger);    }catch(SchedulerException e){     //启动验证失败,设置任务标记为禁用     e.printStackTrace();     rj.setEnabled(ReportJobConstants.FALSE_STRING);     baseDao.updateObject(rj);    }   }  }  //初始化任务只需要执行一次,执行一次后移除初始化触发器  scheduler.unscheduleJob("InitTrigger", Scheduler.DEFAULT_GROUP);  //任务启动  scheduler.start(); }

(2).所有的触发器执行的任务类均为ReportJobTodo.class,ReportJobTodo需要实现接口:org.quartz.Job中的方法execute方法,参考代码如下:

/**       * 报表生成任务       */      public void execute(JobExecutionContext je) throws JobExecutionException {           //获取触发器名称           String triggerName = je.getTrigger().getName();           //根据触发器名称得到对应的任务Id           Long reportJobGuId = Long.valueOf(triggerName.split("_")[1]);           //获取任务           ReportJob rj = (ReportJob)baseDao.loadObject(ReportJob.class, reportJobGuId);           //获取任务细节列表           String hql = "from ReportJobDetail t where reportJobGuId = ?";           List list = baseDao.selectByHql(hql, new Object[]{reportJobGuId});           if(null != list && list.size() > 0){               Iterator ite = list.iterator();               while(ite.hasNext()){                   //任务细节对象                   ReportJobDetail rjd = (ReportJobDetail)ite.next();                   //根据获取的任务对象来做具体操作                   //something to do               }           }           //如果有需要,可以将执行过的任务移除           //try {           //  je.getScheduler().unscheduleJob(triggerName, je.getTrigger().getGroup());           //} catch (SchedulerException e) {           //  throw new BusinessException(e.getMessage());           //}       }  /**  * 报表生成任务  */ public void execute(JobExecutionContext je) throws JobExecutionException {  //获取触发器名称  String triggerName = je.getTrigger().getName();  //根据触发器名称得到对应的任务Id  Long reportJobGuId = Long.valueOf(triggerName.split("_")[1]);  //获取任务  ReportJob rj = (ReportJob)baseDao.loadObject(ReportJob.class, reportJobGuId);  //获取任务细节列表  String hql = "from ReportJobDetail t where reportJobGuId = ?";  List list = baseDao.selectByHql(hql, new Object[]{reportJobGuId});  if(null != list && list.size() > 0){   Iterator ite = list.iterator();   while(ite.hasNext()){    //任务细节对象    ReportJobDetail rjd = (ReportJobDetail)ite.next();    //根据获取的任务对象来做具体操作    //something to do   }  }  //如果有需要,可以将执行过的任务移除  //try {  // je.getScheduler().unscheduleJob(triggerName, je.getTrigger().getGroup());  //} catch (SchedulerException e) {  // throw new BusinessException(e.getMessage());  //} }

(3).对于每一个任务提供启用和禁用的功能,启用时将任务加入到任务执行列表中,禁用时移除:

/**       * 启动或禁止任务触发器       * @param condition       * @throws SchedulerException        * @throws ParseException        */      public static void enableTrigger(ReportJobCondition condition) throws SchedulerException, ParseException{           //获取任务对象的HQL语句           String hql = "from ReportJob t where t.guId = ?";           List list = dao.selectByHql(hql, new Object[]{condition.getObjGuId()});           if(null != list && list.size() > 0){               //任务对象               ReportJob rj = (ReportJob)list.get(0);               //定时器表达式               String cronExpression = rj.getCronExpression();               //获取调度工厂对象               SchedulerFactory schedulerFactory = new StdSchedulerFactory();               Scheduler scheduler = schedulerFactory.getScheduler();                              //启动任务               if(ReportJobConstants.TRUE_STRING.equals(condition.getEnabled())){                   //添加任务                   JobDetail jobDetail = new JobDetail("reportJob_" + rj.getGuId(), Scheduler.DEFAULT_GROUP,                               ReportJobTodo.class);                   //添加触发器                   CronTrigger cronTrigger = new CronTrigger("trigger_" + rj.getGuId(), Scheduler.DEFAULT_GROUP);                   //设置定时表达式                   cronTrigger.setCronExpression(cronExpression);                   //启动任务                   scheduler.scheduleJob(jobDetail, cronTrigger);                   rj.setEnabled(ReportJobConstants.TRUE_STRING);                   dao.updateObject(rj);                   dao.flush();               }else{                   //移除触发器                   CronTrigger cronTrigger = (CronTrigger)scheduler.getTrigger("trigger_" + rj.getGuId(), Scheduler.DEFAULT_GROUP);                   if(null != cronTrigger){                       scheduler.unscheduleJob(cronTrigger.getName(), Scheduler.DEFAULT_GROUP);                   }                   rj.setEnabled(ReportJobConstants.FALSE_STRING);                   dao.updateObject(rj);                   dao.flush();               }               //调度器启动               scheduler.start();           }       }  /**  * 启动或禁止任务触发器  * @param condition  * @throws SchedulerException   * @throws ParseException   */ public static void enableTrigger(ReportJobCondition condition) throws SchedulerException, ParseException{  //获取任务对象的HQL语句  String hql = "from ReportJob t where t.guId = ?";  List list = dao.selectByHql(hql, new Object[]{condition.getObjGuId()});  if(null != list && list.size() > 0){   //任务对象   ReportJob rj = (ReportJob)list.get(0);   //定时器表达式   String cronExpression = rj.getCronExpression();   //获取调度工厂对象   SchedulerFactory schedulerFactory = new StdSchedulerFactory();   Scheduler scheduler = schedulerFactory.getScheduler();      //启动任务   if(ReportJobConstants.TRUE_STRING.equals(condition.getEnabled())){    //添加任务    JobDetail jobDetail = new JobDetail("reportJob_" + rj.getGuId(), Scheduler.DEFAULT_GROUP,       ReportJobTodo.class);    //添加触发器    CronTrigger cronTrigger = new CronTrigger("trigger_" + rj.getGuId(), Scheduler.DEFAULT_GROUP);    //设置定时表达式    cronTrigger.setCronExpression(cronExpression);    //启动任务    scheduler.scheduleJob(jobDetail, cronTrigger);    rj.setEnabled(ReportJobConstants.TRUE_STRING);    dao.updateObject(rj);    dao.flush();   }else{    //移除触发器    CronTrigger cronTrigger = (CronTrigger)scheduler.getTrigger("trigger_" + rj.getGuId(), Scheduler.DEFAULT_GROUP);    if(null != cronTrigger){     scheduler.unscheduleJob(cronTrigger.getName(), Scheduler.DEFAULT_GROUP);    }    rj.setEnabled(ReportJobConstants.FALSE_STRING);    dao.updateObject(rj);    dao.flush();   }   //调度器启动   scheduler.start();  } }

cron是一个在UNIX系统上已使用了很长时间的工具,它已经被证明拥有强大的任务调度能力。类CronTrigger就是以cron的这种任务调度能力为基础的。

CronTrigger使用“cron表达式“。cron表达式能够创建任务触发计划,例如“每周一至周五的早晨8点整”或者“每月最后一个周五的下午1点半”。
cron表达式很强大,但是也很容易迷惑。这篇教程的目标就是找出创建cron表达式的难点,使用户在去论坛或邮件列表求助前 ,有一个优先访问的资源。

格式

一个cron表达式是由6至7个字段所组成。这些字段使用空格分隔,可以是任意允许的值。具体见下表:
字段名           必须       有效值                      有效字符
秒                 是          0 至 59                         , - * /
分钟               是          0 至 59                         , - * /
小时               是          0 至 23                         , - * /
Day of month       是          1 至 31                         , - * ? / L W
月                 是          1 至 12 或 JAN 至 DEC           , - * /
Day of week        是          1 至 7 或 SUN 至 SAT            , - * ? / L #
年                 否          空,1970 至 2099                , - * /
所以cron表达式即可以很简单(如:* * * * * ?),
也可以很复杂(如:0 0/5 14,18,3-39,52 ? JAN,MAR,SEP MON-FRI 2002-2010)。
有效字符

  • “*”(所有有效值)——表示该字段所有有效值。例如分钟字段的“*”表示“每一分钟”。

  • “?”(无特定值)——只能在day of month字段与day of week字段出现,且二者只能选一。表示无特定值,可以忽略。例如我想每月10号触发任务,而不管10号是周几。那么就可以在day of month字段设置“10”,而day of week字段设置“?”。具体看下面的例子。

  • “-”——指定一个范围。例如小时字段的“10-12”,就意味着“10点、11点、12点”。

  • “,”——指定额外的值。例如day of week字段的“MON,WED,FRI”,就意味着“星期一、星期三、星期五”。

  • “/”——指定值的增长步长。例如秒字段的“0/15”,就意味着“0秒、15秒、30秒、45秒”。秒字段的“5/15”,就意味着“5秒、20秒、35秒、50秒”。Day of month字段的“1/3”,就意味着“从每个月的1号开始,每三天一次”。

  • “L”(最后)—— 只能在day of month字段与day of week字段出现,且意义不同。例如字段day of month的“L”,就意味着“每月最后一天”——一月31号、非闰年的二月28号。如果使用在day of week字段,就意味着“7”或者“SAT”——周六。但是如果使用在day of week字段的一个值X的后面,就意味着“每月的最后一个星期X”。例如“6L”表示“每月最后一个周五”。需要注意的是,当使用“L”时不能使用“,”、“-”。

  • “W”(工作日)——选择离给定日期最近的工作日(周一至周五)。例如你指定“15W”作为day of month字段的值,就意味着“每个月与15号最近的工作日”。所以,如果15号是周六,则触发器会在14号(周五)触发。如果15号是周日,则触发器会在16号(周一)触发。如果15号是周二,则触发器会在15号(周二)触发。但是,如果你指定“1W”作为day of month字段的值,且1号是周六,则触发器会在3号(周一)触发。quartz不会“跳出”月份的界限。需要注意的是,当使用“W”时不能使用“,”、“-”。

“L”与“W”可在day of month字段联合使用“LW”,表到“每月的最后一个工作日”

  • “#”——指定每月第几个星期X。例如day of week字段的“6#3”,就意味着“每月第3个星期五”(day3=星期五,#3=第三个);“2#1”就意味着“每月第1个星期一”;“4#5”就意味着“每月第5个星期3。需要注意的是“#5”,如果在当月没有第5个星期三,则触发器不会触发。

合法的有效字符(如月份名字与星期名字)是大小写不敏感的。MON与mon是相同的。

例子
以下是一些完整的例子:
cron表达式                  含义
0 0 12 * * ?                   每天12点整触发一次
0 15 10 ? * *                  每天10点15分触发一次
0 15 10 * * ?                  每天10点15分触发一次
0 15 10 * * ? *                每天10点15分触发一次
0 15 10 * * ? 2005             2005年内每天10点15分触发一次
0 * 14 * * ?                   每天的2点整至2点59分,每分钟触发一次
0 0/5 14 * * ?                 每天的2点整至2点55分,每5分钟触发一次
0 0/5 14,18 * * ?              每天的2点整至2点55分以及18点整至18点55分,每5分钟触发一次
0 0-5 14 * * ?                 每天的2点整至2点5分,每分钟触发一次
0 10,44 14 ? 3 WED             每年3月的每个星期三的2点10分以及2点44分触发一次
0 15 10 ? * MON-FRI            每月周一、周二、周三、周四、周五的10点15分触发一次
0 15 10 15 * ?                 每月15的10点15分触发一次
0 15 10 L * ?                  每月最后一天的10点15分触发一次
0 15 10 ? * 6L                 每月最后一个周五的10点15分触发一次
0 15 10 ? * 6L                 每月最后一个周五的10点15分触发一次
0 15 10 ? * 6L 2002-2005       2002年至2005年间,每月最后一个周五的10点15分触发一次
0 15 10 ? * 6#3                每月第三个周五的10点15触发一次
0 0 12 1/5 * ?                 每月1号开始,每5天的12点整触发一次

0 11 11 11 11 ?                每年11月11日11点11分触发一次

参考资料:

1.cronExpression介绍:http://en.wikipedia.org/wiki/CRON_expression

http://jlusdy.javaeye.com/blog/87044

2.quartz官方文档:http://www.quartz-scheduler.org/docs/index.html

转载于:https://my.oschina.net/ydsakyclguozi/blog/472307

你可能感兴趣的文章
css等高布局技巧
查看>>
你绝不能错过的效率神器 —— Alfred
查看>>
009-事务管理
查看>>
泛型的继承和通配符,同时归纳集合部分的面试点
查看>>
VS 之 InstallShield Limited Edition for Visual Studio 2015 图文教程
查看>>
爬虫如何解决验证码的问题
查看>>
keepalived实现服务高可用
查看>>
(四)Linux Shell编程——输入输出重定向
查看>>
JSP的C标签遍历Map数据
查看>>
WPF 一个弧形手势提示动画
查看>>
【网络文摘】一位36岁程序员的困惑
查看>>
Python3将ipa包中的文件按大小排序
查看>>
【xshell】xshell设置快捷键 设置Ctrl+C Ctrl+V快捷键为复制粘贴
查看>>
Sql Server 2008中存储过程传入表值参数
查看>>
position absolute relative z-index
查看>>
HDOJ-1572 路线最短问题[深搜]
查看>>
【转】使用crontab需要注意的几个问题
查看>>
【数据结构】顺序线性表的构造和存储数据
查看>>
WinRT控件:图表、图示、地图和其他
查看>>
在Asp.Net中使用JQueryEasyUI
查看>>