天天看點

Quartz在Spring中設定動态定時任務

什麼是動态定時任務:

是由客戶制定生成的,服務端隻知道該去執行什麼任務,但任務的定時是不确定的(是由客戶制定)。這樣總不能修改配置檔案每定制個定時任務就增加一個trigger吧,即便允許客戶修改配置檔案,但總需要重新啟動web服務啊,研究了下Quartz在Spring中的動态定時,發現: cronExpression是關鍵,如果可以動态設定cronExpression的值,也就說如果我們可以直接調用CronTriggerBean中設定cronExpression的方法,就可以順利解決問題了。

① targetMethod: 指定需要定時執行scheduleInfoManager中的simpleJobTest()方法

② concurrent:對于相同的JobDetail,當指定多個Trigger時, 很可能第一個job完成之前,第二個job就開始了。指定concurrent設為false,多個job不會并發運作,第二個job将不會在第一個job完成之前開始。

③ cronExpression:0/10 * * * * ?表示每10秒執行一次,具體可參考附表。

④ triggers:通過再添加其他的ref元素可在list中放置多個觸發器。scheduleInfoManager中的simpleJobTest()方法注意:此方法沒有參數,如果scheduleInfoManager有兩個方法simpleJobTest()和simpleJobTest(String argument),則spring隻會去執行無參的simpleJobTest().

public void simpleJobTest()

{         

   log.warn("uh oh, Job is scheduled !'" + "' Success...");    

}

   Quartz在Spring中動态設定cronTrigger方法一Spring配置檔案:

将定時器注入到業務邏輯層Manager

<bean id="scheduleInfoManager" class="com.lively.happyoa.jobs.webapp.manager.scheduleInfoManager">  
         <property name="scheduler" ref="schedulerFactory"/>  
     </bean>  
    <bean id="schedulerJobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">  
         <property name="targetObject" ref="scheduleInfoManager"/>  
         <property name="targetMethod" value="reScheduleJob"/>  
         <property name="concurrent" value="false"/>  
    </bean>  
    <bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean" >  
          <property name="jobDetail" ref="schedulerJobDetail"/>  
          <property name="cronExpression">  
              <value>0/10 * * * * ?</value>  
          </property>  
      </bean>  
     <bean id="schedulerFactory" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">  
         <property name="triggers">  
             <list>  
                <ref local="cronTrigger"/>  
             </list>  
         </property>  
</bean>        

  scheduleInfoManager中的reScheduleJob ()方法及相關方法① reScheduleJob讀取資料庫,獲得自定義定時器排程時間():

private void reScheduleJob() throws SchedulerException, ParseException {   
        // 運作時可通過動态注入的scheduler得到trigger   
        CronTriggerBean trigger = (CronTriggerBean) scheduler.getTrigger(                "cronTrigger", Scheduler.DEFAULT_GROUP);   
         String dbCronExpression = getCronExpressionFromDB();   
         String originConExpression = trigger.getCronExpression();   
     // 判斷從DB中取得的任務時間(dbCronExpression)和現在的quartz線程中的任務時間(originConExpression)是否相等   
     // 如果相等,則表示使用者并沒有重新設定資料庫中的任務時間,這種情況不需要重新rescheduleJob   
         if(!originConExpression.equalsIgnoreCase(dbCronExpression)){   
            trigger.setCronExpression(dbCronExpression);   
            scheduler.rescheduleJob("cronTrigger", Scheduler.DEFAULT_GROUP, trigger);   
         }   
     // 下面是具體的job内容,可自行設定   
     // executeJobDetail();}  
           

 ② getCronExpressionFromDB():從資料庫中獲得dbCronExpression的具體代碼,由于使用了scheduleInfoManager,是以要在定義相應的setter方法:

private String getCronExpressionFromDB(){   
         String sql="from ScheduleInfo scheduleInfo where 1=1 ";   
         sql=sql+" and scheduleInfo.infoId = '"+"1" + "'";   
         List scheduleList = scheduleInfoManager.queryScheduleInListBySql(sql);   
         ScheduleInfo scheduleInfo = (ScheduleInfo)scheduleList.get(0);   
         String dbCronExpression = scheduleInfo.getCronExpression();   
        return dbCronExpression;   
}  
           

 ③ 在spring配置檔案的scheduleInfoManager配置了相應的property(scheduler/

scheduleInfoManager),要為其設定setter方法:    
private Scheduler scheduler;   
     // 設值注入,通過setter方法傳入被調用者的執行個體scheduler   
     public void setScheduler(Scheduler scheduler) {   
         this.scheduler = scheduler;   
    }   
     private ScheduleInfoManager scheduleInfoManager;   
    // 設值注入,通過setter方法傳入被調用者的執行個體scheduleInfoManager   
    public void setScheduleInfoManager(ScheduleInfoManager scheduleInfoManager){   
         this.scheduleInfoManager = scheduleInfoManager;   
    }  
           

Quartz 在Spring中動态設定cronTrigger方法二在上面的2中我們可以看到,盡管已經可以動态進行rescheduleJob了,不過依然需要我們設定一個cronExpression,如果嘗試一下拿掉spring配置中的        

<property name="cronExpression">

              <value>0/10 * * * * ?</value>

</property>

則容器(如tomcat)啟動時會報錯。實際中我們希望tomcat啟動時就可以直接去讀資料庫,拿到相應的dbCronExpression,然後定時執行一個job,而不希望配置初始的cronExpression ,觀察下面的CronTriggerBean,考慮到cronExpression需要初始化,如果設定一個類InitializingCronTrigger繼承CronTriggerBean,然後在這個類中做一些讀取DB的初始化工作(設定cronExpression),問題就可以解決了。Spring配置檔案:

<bean id="scheduleInfoManager" class="com.lively.happyoa.jobs.webapp.manager.ScheduleInfoManager">         <property name="scheduler" ref="schedulerFactory"/>  
     </bean>  
     <bean id="schedulerJobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">  
        <property name="targetObject" ref="scheduleInfoManager"/>  
         <property name="targetMethod" value="reScheduleJob"/>  
         <property name="concurrent" value="false"/>  
     </bean>  
   <bean id="cronTrigger" class="com.lively.happyoa.jobs.webapp.manager.ScheduleInfoManager.InitializingCronTrigger">  
         <property name="jobDetail" ref="schedulerJobDetail"/>  
         <!--<property name="cronExpression">  
              <value>0/10 * * * * ?</value>  
          </property>-->  
         <property name="scheduleInfoManager" ref="scheduleInfoManager"/>  
      </bean>  
     <bean id="schedulerFactory" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">  
         <property name="triggers">  
             <list>  
                <ref local="cronTrigger"/>  
            </list>  
         </property>  
</bean>        

 InitializingCronTrigger中的相關方法注意:在注入scheduleInfoManager屬性的時候,我們可以去讀取DB任務時間(之是以放在setter方法中,是因為需要在設定scheduleInfoManager後進行getCronExpressionFromDB(),否則,也可以①②邏輯把放在類的構造函數中).注意InitializingCronTrigger必須extends CronTriggerBean.

public class InitializingCronTrigger extends CronTriggerBean implements Serializable {   
     private ScheduleInfoManager scheduleInfoManager;   
     // 設值注入,通過setter方法傳入被調用者的執行個體scheduleInfoManager   
     public void setScheduleInfoManager(ScheduleInfoManager scheduleInfoManager){   
         this.scheduleInfoManager = scheduleInfoManager;   
         // 因為在getCronExpressionFromDB使用到了scheduleInfoManager,是以   
         // 必須上一行代碼設定scheduleInfoManager後進行   
getCronExpressionFromDB   
         String cronExpression = getCronExpressionFromDB ();   
   // ①   
         // 因為extends CronTriggerBean ,此處調用父類方法初始化cronExpression   
        setCronExpression(cronExpression);   
                     // ②}   
    private String getCronExpressionFromDB(){   
        String sql="from ScheduleInfo scheduleInfo where   
 1=1 ";   
         sql=sql+" and scheduleInfo.infoId = '"+"1" + "'";   
         List scheduleList = scheduleInfoManager.queryScheduleInListBySql(sql);   
         ScheduleInfo scheduleInfo = (ScheduleInfo)scheduleList.get(0);   
        String dbCronExpression = scheduleInfo.getCronExpression();   
         return dbCronExpression;}……}  
           

Quartz 中時間參數說明 即Cron表達式

Cron表達式

Quartz 使用類似于Linux下的Cron表達式定義時間規則,Cron表達式由6或7個由空格分隔的時間字段組成,如表1所示:

表1 Cron表達式時間字段

位置
時間域名 允許值 允許的特殊字元
1 秒 0-59 , - * /
2 分鐘 0-59 , - * /
3 小時 0-23 , - * /
4 日期 1-31 , - * ? / L W C
5 月份 1-12 , - * /
6 星期 1-7 , - * ? / L C #
7 年(可選) 空值1970-2099 , - * /

Cron表達式的時間字段除允許設定數值外,還可使用一些特殊的字元,提供清單、範圍、通配符等功能,細說如下:

●星号(*):可用在所有字段中,表示對應時間域的每一個時刻,例如,*在分鐘字段時,表示“每分鐘”;

●問号(?):該字元隻在日期和星期字段中使用,它通常指定為“無意義的值”,相當于點位符;

●減号(-):表達一個範圍,如在小時字段中使用“10-12”,則表示從10到12點,即10,11,12;

●逗号(,):表達一個清單值,如在星期字段中使用“MON,WED,FRI”,則表示星期一,星期三和星期五;

●斜杠(/):x/y表達一個等步長序列,x為起始值,y為增量步長值。如在分鐘字段中使用0/15,則表示為0,15,30和45秒,而5/15在分鐘字段中表示5,20,35,50,你也可以使用*/y,它等同于0/y;

●L:該字元隻在日期和星期字段中使用,代表“Last”的意思,但它在兩個字段中意思不同。L在日期字段中,表示這個月份的最後一天,如一月的31号,非閏年二月的28号;如果L用在星期中,則表示星期六,等同于7。但是,如果L出現在星期字段裡,而且在前面有一個數值X,則表示“這個月的最後X天”,例如,6L表示該月的最後星期五;

●W:該字元隻能出現在日期字段裡,是對前導日期的修飾,表示離該日期最近的工作日。例如15W表示離該月15号最近的工作日,如果該月15号是星期六,則比對14号星期五;如果15日是星期日,則比對16号星期一;如果15号是星期二,那結果就是15号星期二。但必須注意關聯的比對日期不能夠跨月,如你指定1W,如果1号是星期六,結果比對的是3号星期一,而非上個月最後的那天。W字元串隻能指定單一日期,而不能指定日期範圍;

●LW組合:在日期字段可以組合使用LW,它的意思是當月的最後一個工作日;

●井号(#):該字元隻能在星期字段中使用,表示當月某個工作日。如6#3表示當月的第三個星期五(6表示星期五,#3表示目前的第三個),而4#5表示當月的第五個星期三,假設當月沒有第五個星期三,忽略不觸發;

● C:該字元隻在日期和星期字段中使用,代表“Calendar”的意思。它的意思是計劃所關聯的日期,如果日期沒有被關聯,則相當于月曆中所有日期。例如5C在日期字段中就相當于月曆5日以後的第一天。1C在星期字段中相當于星期日後的第一天。

Cron表達式對特殊字元的大小寫不敏感,對代表星期的縮寫英文大小寫也不敏感。

表2下面給出一些完整的Cron表示式的執行個體:

表2 Cron表示式示例

表示式

說明

"0 0 12 * * ? " 每天12點運作
"0 15 10 ? * *" 每天10:15運作
"0 15 10 * * ?" 每天10:15運作
"0 15 10 * * ? *" 每天10:15運作
"0 15 10 * * ? 2008" 在2008年的每天10:15運作
"0 * 14 * * ?" 每天14點到15點之間每分鐘運作一次,開始于14:00,結束于14:59。
"0 0/5 14 * * ?" 每天14點到15點每5分鐘運作一次,開始于14:00,結束于14:55。
"0 0/5 14,18 * * ?" 每天14點到15點每5分鐘運作一次,此外每天18點到19點每5鐘也運作一次。
"0 0-5 14 * * ?" 每天14:00點到14:05,每分鐘運作一次。
"0 10,44 14 ? 3 WED" 3月每周三的14:10分到14: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 2007-2009" 在2007,2008,2009年每個月的最後一個星期五的10:15分運作。
"0 15 10 ? * 6#3" 每月第三個星期五的10:15分運作。

注意:CronTriggerBean的cronExpression屬性指定格式:至少6個時間元素,上面表示每天的15:37-38分執行1次任務。

時間元素(按照順序):

秒(0-59):

分(0-59):

小時(0-23):

每月第幾天(1-31):

月(1-12或JAN-DEC):

每星期第幾天(1-7或SUN-SAT):

年(1970-2099):

其中:不用設定的用”?”.

(1)每月第幾天和每星期第幾天是互斥的,兩個隻能設定1個。如果有好幾個時間點,可以使用”,”符号,例如:”0 0 10,12,14 * * ?”表示每天的10時,12時,14時執行Job.

(2)對于連續時間可以使用”-”符号。例如:”0 10,12,14 1-15 * ?”表示每月的1到15日10時,12時,14時執行Job.

(3)時間格式的年可以不用制定。例如”0 0 10,12,14 ? MON 2006”表示2006年每星期一的10時,12時,14時執行Job.