最近看Spring,發現Spring有任務調度管理功能,能很好地解決以上的問題。於是我們決定將定時程序遷移到Spring中。下面就結合我們這次程序的遷移,介紹一下如何使用Spring的任務調度。
在討論Spring的任務調度前,我們先談談Spring。仔細探究Spring,你會發現很多有趣的東西。第一,Spring中並沒有多少新技術,就如AOP這些概念,其實於上個****早已存在了。 MS的操作系統 window2000及以後版本的MTS(Microsoft Transaction Server,事務服務器)就是使用AOP實現的。至於更早的如TUXEDO之類的交易中間件,我想在其中也應該有着AOP理念。第二,Spring並沒有實現很多功能,它只是集成了很多功能。如數據層,它可以集成Hibernate或iBATIS;MVC框架,它可以集成Struts;郵件收發,它集成了JavaMail;包括下面我們將要討論的Spring任務調度,它也是集成了第三方的任務調度類庫:JDK自帶的Timer類庫或第三方的Quartz類庫。Spring只是做了個抽象的接口和集成,方便我們調用。Sping看起來很龐大,可以做很多事情,其實它什麼事都不做!這可能也是它的“輕量級”含義所在的吧。
在Spring中,使用JDK的Timer類庫來做任務調度功能不是很方便,關鍵它不可以象cron服務那樣可以指定具體年、月、日、時和分的時間。你只能將時間通過換算成微秒後傳給它。如任務是每天執行一次,則需要在spring中如下配置:
<bean id="scheduledTask"class= "org.springframework.scheduling.timer.ScheduledTimerTask">
<!--程序啓動後開始執行任務的延遲時間 -->
<property name="delay"value="0" />
<!--每隔一天【一天=24×60×60×1000微秒】執行一次-->
<property name="period"value="86400000" />
<!--業務統計報表bean -->
<property name="timerTask"ref="businessReport" />
</bean>
其中period就是一天的微秒數。如果每月1日運行一次,那就複雜了,不知如何配置。因爲月份有大、小月之分,每月的微秒數都不一樣。
而Quartz類庫不但有着上述JDK的Timer類庫類似的配置,更重要的,它還有着類似於unix的cron服務的配置。因此,在遷移中我們採用了Quartz類庫的接口。
Quartz可以通過兩種方式來調度程序:一是使用Spring提供的MethodInvokingJobDetailFactoryBean代理類,Quartz通過該代理類直接調度任務類的某個函數;二是任務類繼承並實現Quartz接口,Quartz通過該接口進行調度。
如果採用第一種方式,即由Quartz直接調度任務類的某個接口,那麼,業務類是不必進行任何修改的。我們的業務類大概如下
public class BusinessReport {
public void perform(){ //執行報表統計入口函數
//業務邏輯
}
}
第一步,在Spring配置文件中增加本業務類
<bean id=" businessReport "class=" BusinessReport "/>
第二步,定義任務。在Spring配置文件中配置代理類MethodInvokingJobDetailFactoryBean,定義任務的詳細信息。
<bean id=" reportTask " class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name=" targetObject" ref=" businessReport " />
<property name=" targetMethod" value=" perform " />
<property name=" concurrent "value=" false " />
</bean>
這個配置告訴Spring,我們的任務是執行id爲businessReport的bean中的perform函數。其中參數concurrent告訴Spring,不要併發運行這個任務。
第三步,配置一個觸發器。在Spring配置文件中配置觸發器類CronTriggerBean 。
<bean id="cronTrigger"class="org.springframework.scheduling.quartz.CronTriggerBean">
<property name="jobDetail"ref=" reportTask " />
<property name="cronExpression"value="0 0 1 1 * ?" />
</bean>
觸發器將告訴Quartz兩件事:在何時觸發任務、觸發哪個任務。其中屬性參數cronExpression爲調度時間,格式和unix上的crontab類似,具體見下表1。“0 0 1 1 * ?”表示每月1日凌晨1點運行。其中問號表示忽略該位置(星期)上的值。屬性參數jobDetail指向具體的任務bean:reportTask 。如果你有多個任務,每個任務的觸發時間都不一樣,則你可以在此配置多個不同的觸發器。
表1. cronExpression的時間格式
位置
含義
1
秒(0–59)
2
分(0–59)
3
時(0–23)
4
日(1–31)
5
月(1–12)
6
星期(SUN–SAT or 1–7)
7
年(可選, 1970–2099)
第四步,配置一個調度器。在Spring配置文件中配置調度器類SchedulerFactoryBean。
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<ref bean="cronTrigger" />
</list>
</property>
</bean>
該調度器用於管理觸發器。只有在調度器中列表出現的觸發器才被Quartz系統調度執行。至此,所有的配置已完成,任務已能正常跑了。
如果採用第二種方式,那業務類是要進行小小的修改。整個過程如下。
第一步,修改上述的業務類,修改如下:
public class BusinessReport implementsorg.quartz.Job {//繼承quartz 的job接口
//實現job接口的execute函數,在其中簡單調用perform()函數就可以了。
public voidexecute(org.quartz.JobExecutionContext context){
//執行報表統計入口函數
perform()
}
//其它的保持不變。
public void perform(){ //執行報表統計入口函數
//業務邏輯
}
}
修改過程比較簡單,只增加了兩三行代碼。
第二步,定義任務。在Spring配置文件中配置如下任務的詳細信息。
<bean name=" reportTask "class= "org.springframework.scheduling.quartz.JobDetailBean">
<property name="jobClass"value=" BusinessReport " />
</bean>
此配置告訴Quartz,我們的任務類的名字爲BusinessReport。在定時觸發時,Quartz會利用該類名來創建任務的實例,並執行該實例的execute方法。
第三、第四步與第一種的調度方式相應的步驟一樣。
按第二種方式,整個Spring的配置文件如下:
<!DOCTYPE beans PUBLIC"-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<!-- 我們的任務 -->
<bean name=" reportTask "class= "org.springframework.scheduling.quartz.JobDetailBean">
<property name="jobClass"value=" BusinessReport " />
</bean>
<!-- 觸發器 -->
<bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
<!-- 指向我們的任務 -->
<property name="jobDetail"ref=" reportTask " />
<!-- 每月1日凌晨1點運行 -->
<propertyname="cronExpression" value="0 0 1 1 * ?" />
</bean>
<!-- 調度器 -->
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list><!-- 觸發器列表-->
<ref bean="cronTrigger" />
</list>
</property>
</bean>
</beans>
按照上述步驟,整個遷移十分順利,程序也穩定運行。
到Quartz的官方網站溜達了一下,發現Quartz還有不少強大的功能,包括任務的持久化、事務化、容錯、負載均衡等功能。但這些功能暫時在項目中都用不上。
Spring的任務調度管理就介紹到這裏。Spring還有很多其它實用的功能,如果我們能拿過來用在項目中,不但加快開發進度,而且還可以保證項目質量。