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