版權(quán)所有:(xiaodaoxiaodao)藍(lán)小刀
??
xiaodaoxiaodao@gmail.com
http://www.aygfsteel.com/xiaodaoxiaodao/articles/103437.html
?
?
?
?
轉(zhuǎn)載請(qǐng)注明來(lái)源/作者
Quartz
在Spring中動(dòng)態(tài)設(shè)置cronExpression
什么是動(dòng)態(tài)定時(shí)任務(wù):是由客戶制定生成的,服務(wù)端只知道該去執(zhí)行什么任務(wù),但任務(wù)的定時(shí)是不確定的(是由客戶制定)。
這樣總不能修改配置文件每定制個(gè)定時(shí)任務(wù)就增加一個(gè)trigger吧,即便允許客戶修改配置文件,但總需要重新啟動(dòng)web服務(wù)啊,研究了下Quartz在Spring中的動(dòng)態(tài)定時(shí),發(fā)現(xiàn)<bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean" >
???????? <property name="jobDetail" ref="schedulerJobDetail"/>
???????? <property name="cronExpression">
???????????? <value>0/10 * * * * ?</value>
???????? </property>
???
?
中cronExpression是關(guān)鍵,如果可以動(dòng)態(tài)設(shè)置cronExpression的值,也就說(shuō)如果我們可以直接調(diào)用CronTriggerBean中設(shè)置cronExpression的方法,就可以順利解決問(wèn)題了。
熟悉1的朋友可以跳過(guò)不看,下面2、3是動(dòng)態(tài)定時(shí)任務(wù)的具體實(shí)現(xiàn)。
1.?
Quartz
在Spring中的簡(jiǎn)單配置
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>
在上面的配置中設(shè)定了
① targetMethod: 指定需要定時(shí)執(zhí)行scheduleInfoAction中的simpleJobTest()方法
② concurrent:對(duì)于相同的JobDetail,當(dāng)指定多個(gè)Trigger時(shí), 很可能第一個(gè)job完成之前,第二個(gè)job就開始了。指定concurrent設(shè)為false,多個(gè)job不會(huì)并發(fā)運(yùn)行,第二個(gè)job將不會(huì)在第一個(gè)job完成之前開始。
③ cronExpression:0/10 * * * * ?表示每10秒執(zhí)行一次,具體可參考附表。
④ triggers:通過(guò)再添加其他的ref元素可在list中放置多個(gè)觸發(fā)器。
scheduleInfoAction
中的simpleJobTest()方法
注意:此方法沒(méi)有參數(shù),如果scheduleInfoAction有兩個(gè)方法simpleJobTest()和simpleJobTest(String argument),則spring只會(huì)去執(zhí)行無(wú)參的simpleJobTest().
public void simpleJobTest() {?
??????? log.warn("uh oh, Job is scheduled !'" + "' Success...");
??? }
2
.Quartz在Spring中動(dòng)態(tài)設(shè)置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 ()方法及相關(guān)方法
① reScheduleJob():讀取數(shù)據(jù)庫(kù),獲得自定義定時(shí)器調(diào)度時(shí)間
??? private void reScheduleJob() throws SchedulerException, ParseException {
??????? //
運(yùn)行時(shí)可通過(guò)動(dòng)態(tài)注入的scheduler得到trigger
??????? CronTriggerBean trigger = (CronTriggerBean) scheduler.getTrigger(
?
?????????????? "cronTrigger", Scheduler.DEFAULT_GROUP);
??????? String dbCronExpression = getCronExpressionFromDB();
??????? String originConExpression = trigger.getCronExpression();
??? //
判斷從DB中取得的任務(wù)時(shí)間(dbCronExpression)和現(xiàn)在的quartz線程中的任務(wù)時(shí)間(originConExpression)是否相等
??? //
如果相等,則表示用戶并沒(méi)有重新設(shè)定數(shù)據(jù)庫(kù)中的任務(wù)時(shí)間,這種情況不需要重新rescheduleJob
??????? if(!originConExpression.equalsIgnoreCase(dbCronExpression)){
??????????? trigger.setCronExpression(dbCronExpression);
??????????? scheduler.rescheduleJob("cronTrigger", Scheduler.DEFAULT_GROUP, trigger);
??????? }
??? //
下面是具體的job內(nèi)容,可自行設(shè)置
??? // executeJobDetail();
}
② getCronExpressionFromDB():從數(shù)據(jù)庫(kù)中獲得dbCronExpression的具體代碼,由于使用了scheduleInfoManager,所以要在定義相應(yīng)的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配置了相應(yīng)的property(scheduler/scheduleInfoManager),要為其設(shè)置setter方法
??? private Scheduler scheduler;
??? //
設(shè)值注入,通過(guò)setter方法傳入被調(diào)用者的實(shí)例scheduler
??? public void setScheduler(Scheduler scheduler) {
??????? this.scheduler = scheduler;
?
?? }
??? private ScheduleInfoManager scheduleInfoManager;
??? //
設(shè)值注入,通過(guò)setter方法傳入被調(diào)用者的實(shí)例scheduleInfoManager
??? public void setScheduleInfoManager(ScheduleInfoManager scheduleInfoManager){
??????? this.scheduleInfoManager = scheduleInfoManager;
??? }
3.?
Quartz
在Spring中動(dòng)態(tài)設(shè)置cronTrigger方法二
在上面的2中我們可以看到,盡管
已經(jīng)可以動(dòng)態(tài)進(jìn)行
rescheduleJob
了,不過(guò)依然需要我們?cè)O(shè)置一個(gè)
cronExpression
,如果嘗試一下拿掉spring配置中的
???????
<property name="cronExpression">
???????????? <value>0/10 * * * * ?</value>
???????? </property>
則容器(如tomcat)啟動(dòng)時(shí)會(huì)報(bào)錯(cuò)。
實(shí)際中我們希望tomcat啟動(dòng)時(shí)就可以直接去讀數(shù)據(jù)庫(kù),拿到相應(yīng)的
dbCronExpression
,然后定時(shí)執(zhí)行一個(gè)job,而不希望配置初始的
cronExpression
,觀察下面的
CronTriggerBean
,考慮到cronExpression需要初始化,如果設(shè)定一個(gè)類InitializingCronTrigger繼承CronTriggerBean,然后在這個(gè)類中做一些讀取DB的初始化工作(設(shè)置cronExpression),問(wèn)題就可以解決了。
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
中的相關(guān)方法
注意:在注入scheduleInfoManager屬性的時(shí)候,我們可以去讀取DB任務(wù)時(shí)間(之所以放在setter方法中,是因?yàn)樾枰谠O(shè)置scheduleInfoManager后進(jìn)行getCronExpressionFromDB(),否則,也可以①②邏輯把放在類的構(gòu)造函數(shù)中).
注意InitializingCronTrigger必須extendsCronTriggerBean.
public class InitializingCronTrigger extendsCronTriggerBean implements Serializable {
??? private ScheduleInfoManager scheduleInfoManager;
??? //
設(shè)值注入,通過(guò)setter方法傳入被調(diào)用者的實(shí)例scheduleInfoManager
??? public void setScheduleInfoManager(ScheduleInfoManager scheduleInfoManager){
??????? this.scheduleInfoManager = scheduleInfoManager;
??????? //
因?yàn)樵?span lang="EN-US" style="BACKGROUND: silver; mso-highlight: silver">getCronExpressionFromDB使用到了scheduleInfoManager,所以
??????? //
必須上一行代碼設(shè)置scheduleInfoManager后進(jìn)行getCronExpressionFromDB
??????? String cronExpression = getCronExpressionFromDB ();?? //
①
??????? //
因?yàn)?span lang="EN-US" style="COLOR: fuchsia">extendsCronTriggerBean ,此處調(diào)用父類方法初始化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(
來(lái)自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
|
版權(quán)所有:(xiaodaoxiaodao)藍(lán)小刀
??
xiaodaoxiaodao@gmail.com
// 設(shè)值注入,通過(guò)setter方法傳入被調(diào)用者的實(shí)例scheduler
public void setScheduler(Scheduler scheduler) {
this.scheduler = scheduler;
}
scheduler的類型是Scheduler ,但是你卻注入了下面這個(gè)類的實(shí)例org.springframework.scheduling.quartz.SchedulerFactoryBean
能否解釋一下?
查看org.springframework.scheduling.quartz.SchedulerFactoryBean源碼,其中有一個(gè)getObject方法
public Object getObject() {
return scheduler;
}
返回的scheduler對(duì)象實(shí)現(xiàn)了org.quartz.scheduler.Scheduler接口~~
SchedulerFactoryBean是一種工廠bean,區(qū)別于普通bean的是,在工廠Bean中spring通過(guò)getObject方法返回真正的bean對(duì)象~~
非抄襲,當(dāng)時(shí)我找了很多網(wǎng)上帖子都沒(méi)有相關(guān)的解決辦法,后來(lái)覆蓋了CronTriggerBean中的方法才解決的~~
原創(chuàng)的啦,拜托先看一看再謾罵,真的很無(wú)聊啊兄弟~~
光看配置文件都有問(wèn)題啊..
scheduleInfoManager是自己定義的類,一般屬于manager層,在action和dao層之間~~
<bean id="scheduleInfoAction" class="com.lively.happyoa.jobs.webapp.action.ScheduleInfoAction">
會(huì)報(bào)錯(cuò):
Error creating bean with name 'schedulerFactoryBean':
FactoryBean which is currently in creation returned null from getObject
一個(gè)Action是OK的
不知你那有沒(méi)有這個(gè)問(wèn)題??
我也郁悶中。。
while setting bean property 'jobDetail'。。。。。
FactoryBean which is currently in creation returned null from getObject
一個(gè)Action是OK的
不知你那有沒(méi)有這個(gè)問(wèn)題??
我這也是,一個(gè)用一個(gè)action寫例子的時(shí)候可以,加到項(xiàng)目中就報(bào)錯(cuò),求解呀
reScheduleJob
executeJobDetail