Spring quartz定時器動態多任務實現

 項目中常常會碰到須要定時執行的任務,而且須要執行什麼任務,以及任務執行的時間都由用戶自定義的需求。quartz是比較經常使用的定時器工具,而且在spring框架中也已經作了很好的集成,因此在以spring+hibernate+struts的主流架構中,咱們能夠採用quartz來作定時器任務的解決方案,下面,咱們來看下如何在項目中使用quartz來作動態多任務定時器功能。html

      1.簡單單任務定時器的spring配置java

<!-- 配置定時任務,用於初始化定時器 -->  
    <bean id="InitJobDetail"  
        class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">  
        <property name="targetObject">  
            <ref bean="ReportJobTodo"/>  
        </property>  
        <property name="targetMethod">  
            <value>initJobTrigger</value>  
        </property>  
        <property name="concurrent" value ="false"/>    
    </bean>  
    <bean id="ReportJobTodo"  
        class="cn.com.gsoft.report.timetask.ReportJobTodo">  
    </bean>  
    <bean id="InitTrigger"  
        class="org.springframework.scheduling.quartz.CronTriggerBean">  
        <property name="jobDetail">  
            <ref bean="InitJobDetail"/>  
        </property>  
        <property name="cronExpression">  
            <value>* * * * * ?</value>  
        </property>  
    </bean>  
    <bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">  
        <property name="triggers">  
            <list>  
                <ref local="InitTrigger"/>  
            </list>  
        </property>  
    </bean>  
<!-- 配置定時任務,用於初始化定時器 -->
 <bean id="InitJobDetail"
  class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
  <property name="targetObject">
   <ref bean="ReportJobTodo"/>
  </property>
  <property name="targetMethod">
   <value>initJobTrigger</value>
  </property>
     <property name="concurrent" value ="false"/> 
 </bean>
 <bean id="ReportJobTodo"
  class="cn.com.gsoft.report.timetask.ReportJobTodo">
 </bean>
 <bean id="InitTrigger"
  class="org.springframework.scheduling.quartz.CronTriggerBean">
  <property name="jobDetail">
   <ref bean="InitJobDetail"/>
  </property>
  <property name="cronExpression">
   <value>* * * * * ?</value>
  </property>
 </bean>
 <bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
  <property name="triggers">
   <list>
    <ref local="InitTrigger"/>
   </list>
  </property>
 </bean>

說明:(1).InitJobDetail實例聲明瞭須要執行的任務。其中targetObject說明了須要執行的方法所在的實例對象,targetMethod說明了要執行的方法,concurrent用於說明多個任務是否同步執行。linux

         (2).InitTrigger聲明瞭一個觸發器。jobDetail屬性指明須要執行的任務,cronExpression聲明瞭該任務在何時執行,該表達式跟linux下的crontab定時程序中使用的表達式是同樣的,具體使用方法能夠參考文後的參考資料。spring

         (3).SchedulerFactoryBean中能夠定義多個觸發器,以實現多任務。數據庫

     2.動態多任務實現express

     實現方式:用戶在前臺自行維護任務列表和任務執行時間,後臺將任務執行時間解析成對應的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方法,參考代碼以下:app

/**  
     * 報表生成任務  
     */  
    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」,就意味着「星期1、星期3、星期五」。

  • 「/」——指定值的增加步長。例如秒字段的「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            每個月周1、周2、周3、周4、週五的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

相關文章
相關標籤/搜索