Quartz(一)在Spring中設置動態定時任務

什麼是動態定時任務:
是由客戶制定生成的,服務端只知道該去執行什麼任務,但任務的定時是不確定的(是由客戶制定)。這樣總不能修改配置文件每定製個定時任務就增加一個trigger吧,即便允許客戶修改配置文件,但總需要重新啓動web服務啊,研究了下Quartz在Spring中的動態定時,發現: cronExpression是關鍵,如果可以動態設置cronExpression的值,也就說如果我們可以直接調用CronTriggerBean中設置cronExpression的方法,就可以順利解決問題了。
Spring中使用Quartz的3種方法(MethodInvokingJobDetailFactoryBean,implements Job,extends QuartzJobBean)
以下介紹一下MethodInvokingJobDetailFactoryBean方法
① 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
Xml代碼
<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> 

<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讀取數據庫,獲得自定義定時器調度時間():
Java代碼
private Scheduler scheduler;   
 
// 設值注入,通過setter方法傳入被調用者的實例scheduler  
public void setScheduler(Scheduler scheduler) {     
     this.scheduler = scheduler;     
}     
rivate void reScheduleJob() throws SchedulerException,ParseException {  
        // 運行時可通過動態注入的scheduler得到trigger,注意採用這種注入方式在有的項目中會有問題,如果遇到注入問題,可以採取在運行方法時候,獲得bean來避免錯誤發生。  
        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();} 

 private Scheduler scheduler;

 // 設值注入,通過setter方法傳入被調用者的實例scheduler
 public void setScheduler(Scheduler scheduler) {  
      this.scheduler = scheduler;  
 }  
private void reScheduleJob() throws SchedulerException,ParseException {
         // 運行時可通過動態注入的scheduler得到trigger,注意採用這種注入方式在有的項目中會有問題,如果遇到注入問題,可以採取在運行方法時候,獲得bean來避免錯誤發生。
         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方法:
Java代碼
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;  

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/ Java代碼
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;  
     } 

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配置文件:
Xml代碼
<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> 

<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.
Java代碼
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;}……} 

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.

發佈了108 篇原創文章 · 獲贊 22 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章