spring設置動態定時任務


Quartz 在 Spring 中動態設置 cronExpression

什麼是動態定時任務:是由客戶制定生成的,服務端只知道該去執行什麼任務,但任務的定時是不確定的(是由客戶制定)。

這樣總不能修改配置文件每定製個定時任務就增加一個trigger 吧,即便允許客戶修改配置文件, 但總需要重新啓動web 服務啊,研究了下Quartz 在Spring 中的動態定時,發現<bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean" >

          <property name="jobDetail" ref="schedulerJobDetail"/>

          <property name="cronExpression">

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

          </property>

      中cronExpression 是關鍵,如果可以動態設置cronExpression 的值,也就說如果我們可以直接調用CronTriggerBean 中設置cronExpression 的方法,就可以順利解決問題了。

熟悉1 的朋友可以跳過不看,下面2 、3 是動態定時任務的具體實現。

1 . Quartz 在Spring 中的簡單配置

Spring 配置文件:

     <bean id="schedulerJobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">

         <property name="targetObject" ref="scheduleInfoAction"/>

         <property name="targetMethod " value="simpleJobTest "/>

         <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>

在上面的配置中設定了

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

② concurrent :對於相同的JobDetail ,當指定多個Trigger 時, 很可能第一個job 完成之前,第二個job 就開始了。指定concurrent 設爲false ,多個job 不會併發運行,第二個job 將不會在第一個job 完成之前開始。

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

④ triggers :通過再添加其他的ref 元素可在list 中放置多個觸發器。

scheduleInfoAction 中的simpleJobTest() 方法

注意:此方法沒有參數,如果scheduleInfoAction 有兩個方法simpleJobTest () 和simpleJobTest(String argument),則spring 只會去執行無參的simpleJobTest ().

public void simpleJobTest() {

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

     }

2 .Quartz 在Spring 中動態設置 cronTrigger 方法一

Spring 配置文件:

<bean id="scheduleInfoAction" class="com.lively.happyoa.jobs.webapp.action.ScheduleInfoAction">

         <property name="scheduler " ref="schedulerFactory"/>

         <property name="scheduleInfoManager " ref="scheduleInfoManager"/>

     </bean>

     <bean id="schedulerJobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">

         <property name="targetObject" ref="scheduleInfoAction"/>

         <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>

scheduleInfoAction 中的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 配置文件的scheduleInfoAction 配置了相應的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;

     }

3 . 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="scheduleInfoAction" class="com.lively.happyoa.jobs.webapp.action.ScheduleInfoAction">

         <property name="scheduler " ref="schedulerFactory"/>

         <property name="scheduleInfoManager " ref="scheduleInfoManager"/>

     </bean>

     <bean id="schedulerJobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">

         <property name="targetObject" ref="scheduleInfoAction"/>

         <property name="targetMethod" value="reScheduleJob "/>

         <property name="concurrent" value="false"/>

     </bean>

    <bean id="cronTrigger " class="com.lively.happyoa.jobs.webapp.action.ScheduleInfoAction.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;

}

……

}

附表:

"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 期間的每1 分鐘觸發 
"0 0/5 14 * * ?" 在每天下午2 點到下午2:55 期間的每5 分鐘觸發 
"0 0/5 14,18 * * ?" 在每天下午2 點到2:55 期間和下午6 點到6:55 期間的每5 分鐘觸發 
"0 0-5 14 * * ?" 在每天下午2 點到下午2:05 期間的每1 分鐘觸發 
"0 10,44 14 ? 3 WED" 每年三月的星期三的下午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 2002-2005" 2002 年至2005 年的每月的最後一個星期五上午10:15 觸發 
"0 15 10 ? * 6#3" 每月的第三個星期五上午10:15 觸發

至於每個符號 看看例子就好了. 很簡單了.

以上內容轉載至:http://hi.baidu.com/vip099/blog/item/51bbb03d555f5702bba16766.html

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章