Quartz 是一個強大的企業級 Schedule 工具,也是目前最好的開源 Schedule 工具。Spring中也集成了quartz的應用,下面就講一下如何在spring中使用quartz。
spring的配置:
- <beanid="schedulerFactoryBean"class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
- <propertyname="triggers">
- <list>
- <refbean="simpleTrigger"/>
- <refbean="cornTrigger"/>
- </list>
- </property>
- </bean>
- <beanid="schedulerControl"class="com.pheh.scheduler.Schedule">
- <propertyname="scheduler">
- <refbean="schedulerFactoryBean"/>
- </property>
- </bean>
- <beanid="simpleTrigger"class="org.springframework.scheduling.quartz.SimpleTriggerBean">
- <propertyname="jobDetail">
- <refbean="methodInvokingJobDetail"/>
- </property>
- <propertyname="startDelay">
- <value>1000</value>
- </property>
- <propertyname="repeatInterval">
- <value>3000</value>
- </property>
- </bean>
- <beanid="cornTrigger"class="org.springframework.scheduling.quartz.CronTriggerBean">
- <propertyname="jobDetail">
- <refbean="methodInvokingJobDetail"/>
- </property>
- <propertyname="cronExpression">
- <value>00*/1**?</value>
- </property>
- </bean>
Job:
org.quartz.Job是一個接口,只定義了void execute(JobExecutionContext context)throws JobExecutionException;一個方法。當定時任務被觸發後,系統會自動調用實現了該接口的方法。在spring中,org.springframework.scheduling.quartz.QuartzJobBean對其進行了封裝,使用了Template Method模式。主要是爲了在使用jobDataMap時更加方便。QuartzJobBean有兩個方法,
public final void execute(JobExecutionContext context) throws JobExecutionException
Job接口中定義的,spring在該方法裏進行了些處理,將jobDataMap中的值注入到該Job的實現者中
protected abstract void executeInternal(JobExecutionContext context) throws JobExecutionException
這是一個抽象方法,用戶在擴展了QuartzJobBean後,要自己實現該方法,在其中添加相應的業務邏輯
JobDetail:
JobDetail描述了一個任務具體的信息。在Spring中,JobDetailBean對JobDetail進行了封裝(繼承了JobDetail)。
private String name;//名稱
private String group = Scheduler.DEFAULT_GROUP;//組
private String description;//描述
private Class jobClass;//定時任務觸發時,回調的class,該class要實現Job接口或繼承QuartzJobBean
private JobDataMap jobDataMap;//該任務存儲的數據,在回調的時候也可以使用
private boolean volatility = false;//是否持久化到org.quartz.spi.JobStore中
private boolean durability = false;//當該任務完成後,是否還在JobStore中繼續保留該任務
private boolean shouldRecover = false;//當系統重新啓動後,是否再次執行該任務
對於jobDataMap,它是是一個封裝過的Map,使用方法同Map,如
jobDetailBean.getJobDataMap().put(target,value);
如果使用了QuartzJobBean,在使用jobDetailBean時,可將target的值設成QuartzJobBean的子類的屬性名稱,這樣,在定時觸發時,spring會自動將與target對應的value值注入到QuartzJobBean的子類中去。如。
- ...
- publicclassReminderManagerextendsQuartzJobBean{
- privateStringreminderStr="";
- }
- ...
- jobDetailBean.getJobDataMap().put(reminderStr,"abcdefg");
- ...
這樣當該任務被觸發後,在ReminderManager中,reminderStr的值就會被注入爲"abcdefg"。
Trigger:
trigger就是觸發器。Quartz有個很好的想法就是分離了任務和任務執行的條件。Trigger就是控制任務執行條件的類,當Trigger認爲執行條件滿足的時刻,Trigger會通知相關的Job去執行。分離的好處是:
1.你可以爲某個Job關聯多個Trigger,其中任何一個條件滿足都可以觸發job執行,這樣可以完成一些組合的高級觸發條件
2.當Trigger失效後(比如:一個永遠都不能滿足的條件),你不必去聲明一個新的job,代替的是你可以爲job關聯一個新的Trigger讓job可以繼續執行。
目前的Quartz實現中,存在兩種Trigger,SimpleTrigger和CronTrigger,在spring中分別用SimpleTriggerBean和CronTriggerBean對其進行封裝。SimpleTrigger是簡單觸發器,如從某日到某日,每個一定時間進行一次提醒,在這段時間內進行多少次提醒;CronTrigger是複雜觸發器,用來執行calendar-like的任務,可設定一些複雜的觸發規則,如每年的x月的第y個星期五,或是每個星期天的幾點進行提醒。後面附加一個日常語義與cronTrigger的轉化
Trigger
private String name;//名稱
private String group = Scheduler.DEFAULT_GROUP;//組
private String jobName;//所關聯的jobDetail的名稱
private String jobGroup = Scheduler.DEFAULT_GROUP;//所關聯的jobDetail的組
private String description;//描述
private JobDataMap jobDataMap;//該觸發器存儲的數據,在回調的時候也可以使用
private boolean volatility = false;//是否持久化到org.quartz.spi.JobStore中
SimpleTrigger
private Date startTime = null;//開始日期
private Date endTime = null;//結束日期
private Date nextFireTime = null;//下次的觸發時間
private Date previousFireTime = null;//上次的觸發時間
private int repeatCount = 0;//重複次數
private long repeatInterval = 0;//重複間隔
private int timesTriggered = 0;/已觸發的次數
SimpleTriggerBean
private JobDetail jobDetail;//所關聯的JobDetail,方便在配置文件中使用
CornTrigger
private CronExpression cronEx = null;//觸發條件表達式,它有一個String型的setter
private Date startTime = null;//開始日期
private Date endTime = null;//結束日期
private Date nextFireTime = null;//下次的觸發時間
private Date previousFireTime = null;//上次的觸發時間
private transient TimeZone timeZone = null;//所在時區
CronTriggerBean
private JobDetail jobDetail;//所關聯的JobDetail,方便在配置文件中使用
Scheduler的常用方法
添加一個定時任務:
Date scheduleJob(JobDetail jobDetail,Trigger trigger)
修改一個定時任務,主要是更改trigger:
Date rescheduleJob(String triggerName, String groupName, Trigger newTrigger)
刪除一個定時任務,同時也會將於該jobDetail關聯的trigger一併刪除:
boolean deleteJob(String jobName,String jobGroup)
取得所有的jobDetail組
String[] getJobGroupNames()
取得某個group下的所有的jobDetail
String[] getJobNames(String groupName)
取得指定的jobDetail
JobDetail getJobDetail(String jobName, String jobGroup)
取得指定的jobDetail的所有的Trigger
Trigger[] getTriggersOfJob(String jobName, String groupName)
取得指定的Trigger
Trigger getTrigger(String triggerName, String triggerGroup)
Quartz的存儲:
Quartz默認的是使用RAM存儲所有的信息,但是這樣的話,當我們重啓服務器後,之前的所有的定時任務就全消失了。爲了讓服務器重啓以後,我們的定時任務仍不丟失,我們可採用數據庫持久化定時任務。
首先要先建立數據庫,在quartz-1.6.0/docs/dbTables下,選擇自己使用的數據庫的sql腳本,建立相應的數據庫表。
在WEB-INF下加一個quartz.properties。我們可以在 quartz-1.6.0/examples/example10 中找到該文件的樣例
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX#表明使用JDBC進行持久化
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.jobStore.useProperties = false
org.quartz.jobStore.dataSource = myDS
org.quartz.jobStore.tablePrefix = QRTZ_#該值儘量不要改動,如改動,還要相應的修改sql腳本
org.quartz.jobStore.isClustered = false
org.quartz.dataSource.myDS.driver = net.sourceforge.jtds.jdbc.Driver
org.quartz.dataSource.myDS.URL = jdbc:jtds:sqlserver://192.168.1.101:1433/Northwind;autoReconnect=true
org.quartz.dataSource.myDS.user = sa
org.quartz.dataSource.myDS.password =
org.quartz.dataSource.myDS.maxConnections = 5
日常語義與cronTrigger的轉化,以下setter,getter省略
- publicclassTDateRange{
- privateintstartType=2;//開始類型。默認的使用2表示使用開始日期
- privateDatestartDate=newDate();//開始日期
- privateintendType=0;//結束類型。0表示無結束時間;1表示重複n次後結束;2表示使用結束日期
- privateDateendDate=newDate();//結束日期
- privateintoccurrences;//執行次數
- }
- publicclassTFrequency{
- //0:無重複提醒
- //1:每every
- //2:每個工作日detail=1,2,3,4,5
- //3:每every周後的星期detail日
- //4:每every月的detail日
- //5:每every月的第num1個星期num2
- //6:每年num1月num2日
- //7:每年every月的第num1個月的星期num2
- privateinttype=0;//頻率類型
- privateintevery=0;
- privateStringdetail="";
- privateStringnum1="";
- privateStringnum2="";
- }
- privateStringformatQuartzString(){
- StringquartzStr="";
- tiggernote="";
- //秒分時
- quartzStr="0"+this.dateRange.getStartDate().getMinutes()+""+this.dateRange.getStartDate().getHours()+"";
- switch(this.frequency.getType()){
- case0://無重複提醒
- quartzStr+=this.dateRange.getStartDate().getDate()+""+(this.dateRange.getStartDate().getMonth()+1)+"?"+(this.dateRange.getStartDate().getYear()+1900);
- tiggernote+="起始時間:"+quartzStr;
- break;
- case1://每XX天提醒
- quartzStr+="*/"+this.frequency.getEvery()+"*?";
- tiggernote+="每"+this.frequency.getEvery()+"提醒";
- break;
- case2://每個工作日detail=1,2,3,4,5
- //quartzStr+="?*2-6";
- quartzStr="0*/1***?";//測試
- tiggernote+="每個工作日1,2,3,4,5提醒";
- break;
- case3://每every周後的星期detail日
- quartzStr+="?*"+this.frequency.getDetail()+"/"+this.frequency.getEvery();
- tiggernote+="每"+this.frequency.getEvery()+"周星期"+this.frequency.getDetail()+"日";
- break;
- case4://每every個月的detail日
- quartzStr+=this.frequency.getDetail()+"*/"+this.frequency.getEvery()+"?";
- tiggernote+="每"+this.frequency.getEvery()+"月"+this.frequency.getDetail()+"日";
- break;
- case5://每every個月的第num1個星期num2
- quartzStr+="?*/"+this.frequency.getEvery()+""+this.frequency.getNum2();
- //星期
- if(Integer.valueOf(this.frequency.getNum1()).intValue()>0){
- quartzStr+="#"+this.frequency.getNum1();
- tiggernote+="每"+this.frequency.getEvery()+"月第"+this.frequency.getNum1()+"個星期"+this.frequency.getNum2()+"日";
- }else{
- quartzStr+="L";
- tiggernote+="每"+this.frequency.getEvery()+"月星期"+this.frequency.getNum2();
- }
- break;
- case6://每年num1月num2日
- quartzStr+=this.frequency.getNum2()+""+this.frequency.getNum1()+"?";
- tiggernote+="每年"+this.frequency.getNum1()+"月"+this.frequency.getNum2()+"日";
- break;
- case7://每年every月的第num1個星期num2
- quartzStr+="?"+this.getFrequency().getEvery()+""+this.getFrequency().getNum2();
- //星期
- if(Integer.valueOf(this.frequency.getNum1()).intValue()>0){
- quartzStr+="#"+this.frequency.getNum1();
- tiggernote+="每年"+this.getFrequency().getEvery()+"月的第"+this.frequency.getNum1()+"個星期"+this.getFrequency().getNum2()+"日";
- }else{
- quartzStr+="L";
- tiggernote+="每年"+this.getFrequency().getEvery()+"月的"+this.getFrequency().getNum2()+"日";
- }
- break;
- default:
- }
- log.debug("quartzStr="+quartzStr);
- returnquartzStr;
- }
Scheduler 是一個計劃集,其中可以包含多個 JobDetail 和 Trigger 組成的計劃任務。
在Quartz中,我們可以通過
SchedulerFactory scheduleFactory = new StdSchedulerFactory();
Scheduler scheduler = scheduleFactory.getScheduler();
來取得scheduler,通過調用scheduler.start()來啓動quartz。
在spring中,org.springframework.scheduling.quartz.SchedulerFactoryBean是對Quartz的org.quartz.Scheduler的封裝,通過上面的配置,在spring啓動的時候,quartz就會跟隨着啓動,不需要再用scheduler.start()來啓動。在spring中,如果要取得scheduler,可通過上面的配置文件那樣,將SchedulerFactoryBean注入到schdeuler中