版權所有:(xiaodaoxiaodao)藍小刀
??
xiaodaoxiaodao@gmail.com
http://www.tkk7.com/xiaodaoxiaodao/articles/103437.html
?
?
?
?
轉載請注明來源/作者
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必須extendsCronTriggerBean.
public class InitializingCronTrigger extendsCronTriggerBean implements Serializable {
??? private ScheduleInfoManager scheduleInfoManager;
??? //
設值注入,通過setter方法傳入被調用者的實例scheduleInfoManager
??? public void setScheduleInfoManager(ScheduleInfoManager scheduleInfoManager){
??????? this.scheduleInfoManager = scheduleInfoManager;
??????? //
因為在getCronExpressionFromDB使用到了scheduleInfoManager,所以
??????? //
必須上一行代碼設置scheduleInfoManager后進行getCronExpressionFromDB
??????? String cronExpression = getCronExpressionFromDB ();?? //
①
??????? //
因為extendsCronTriggerBean ,此處調用父類方法初始化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;
?}
……
}
附表
CronTrigger Expression(
來自http://quartz.sourceforge.net/javadoc/org/quartz/CronTrigger.html)
Expression |
Meaning |
"0 0 12 * * ?"
|
Fire at 12pm (noon) every day
|
"0 15 10 ? * *"
|
Fire at 10:15am every day
|
"0 15 10 * * ?"
|
Fire at 10:15am every day
|
"0 15 10 * * ? *"
|
Fire at 10:15am every day
|
"0 15 10 * * ? 2005"
|
Fire at 10:15am every day during the year 2005
|
"0 * 14 * * ?"
|
Fire every minute starting at 2pm and ending at 2:59pm, every day
|
"0 0/5 14 * * ?"
|
Fire every 5 minutes starting at 2pm and ending at 2:55pm, every day
|
"0 0/5 14,18 * * ?"
|
Fire every 5 minutes starting at 2pm and ending at 2:55pm, AND fire every 5 minutes starting at 6pm and ending at 6:55pm, every day
|
"0 0-5 14 * * ?"
|
Fire every minute starting at 2pm and ending at 2:05pm, every day
|
"0 10,44 14 ? 3 WED"
|
Fire at 2:10pm and at 2:44pm every Wednesday in the month of March.
|
"0 15 10 ? * MON-FRI"
|
Fire at 10:15am every Monday, Tuesday, Wednesday, Thursday and Friday
|
"0 15 10 15 * ?"
|
Fire at 10:15am on the 15th day of every month
|
"0 15 |
Fire at 10:15am on the last day of every month
|
"0 15 10 ? * |
Fire at 10:15am on the last Friday of every month
|
"0 15 10 ? * |
Fire at 10:15am on the last Friday of every month
|
"0 15 10 ? * |
Fire at 10:15am on every last friday of every month during the years 2002, 2003, 2004 and 2005
|
"0 15 10 ? * 6#3"
|
Fire at 10:15am on the third Friday of every month
|
版權所有:(xiaodaoxiaodao)藍小刀
??
xiaodaoxiaodao@gmail.com
// 設值注入,通過setter方法傳入被調用者的實例scheduler
public void setScheduler(Scheduler scheduler) {
this.scheduler = scheduler;
}
scheduler的類型是Scheduler ,但是你卻注入了下面這個類的實例org.springframework.scheduling.quartz.SchedulerFactoryBean
能否解釋一下?
查看org.springframework.scheduling.quartz.SchedulerFactoryBean源碼,其中有一個getObject方法
public Object getObject() {
return scheduler;
}
返回的scheduler對象實現了org.quartz.scheduler.Scheduler接口~~
SchedulerFactoryBean是一種工廠bean,區別于普通bean的是,在工廠Bean中spring通過getObject方法返回真正的bean對象~~
非抄襲,當時我找了很多網上帖子都沒有相關的解決辦法,后來覆蓋了CronTriggerBean中的方法才解決的~~
原創的啦,拜托先看一看再謾罵,真的很無聊啊兄弟~~
光看配置文件都有問題啊..
scheduleInfoManager是自己定義的類,一般屬于manager層,在action和dao層之間~~
<bean id="scheduleInfoAction" class="com.lively.happyoa.jobs.webapp.action.ScheduleInfoAction">
會報錯:
Error creating bean with name 'schedulerFactoryBean':
FactoryBean which is currently in creation returned null from getObject
一個Action是OK的
不知你那有沒有這個問題??
我也郁悶中。。
while setting bean property 'jobDetail'。。。。。
FactoryBean which is currently in creation returned null from getObject
一個Action是OK的
不知你那有沒有這個問題??
我這也是,一個用一個action寫例子的時候可以,加到項目中就報錯,求解呀
reScheduleJob
executeJobDetail