1.Spring與Quartz
Quartz是Open Symphony開發的一款開源的任務調度框架。相對於JDK的Timer之類的簡單任務調度程序來說,Quartz擁有更爲全面的功能支持:
1)允許批處理任務狀態的持久化,並且提供不同的持久化策略支持
2)批處理任務的遠程調度
4)集羣支持
5)插件式的可擴展性
2.Quartz擁有明確的角色劃分,分別對應處理任務調度過程中的各種相關關注點:
1)Job:代表將要被調度的任務
2)JobDetail:主要職能是提供Job執行的上下文信息,Job所依賴的某些配置信息,可以通過JobDetail提供,二者通過JobDataMap進行數據交互
3)Trigger:用於觸發被調度任務的執行,可以根據不同的觸發條件,提供不同的Trigger實現
4)Scheduler:Quartz的核心調度程序,負責管理所有相關的Trgger和JobDetail
3.一個Trigger只能用於一個Job,但多個Trigger可以用於同一Job
Quartz中最經常使用的主要的兩種Trigger實現類:
一個是SimpleTrigger,可以指定簡單的基於時間間隔的調度規則
一個是CronTrigger,可以類似於Unix/Linux操作系統中Cron程序多使用的表達式來指定調度規則
public class FXNewsJob implements Job{
private FXNewsProvider fxNewsProvider;
//getXXX();
//setXXX();
public void execute(JobExecutionContext jobExecCtx) throws JobExecutionException{
getFxNewsProvider().getAndPersistNews();
}
}
Trigger simpleTrigger = new SimpleTrigger("triggerName",Scheduler.DEFAULT_GROUP,new Date(),null,SimpleTrigger.REPEAT_INDEFINITELY,60000);
Trigger cronTrigger = new CronTrigger("cronTriggerName",Scheduler.DEFAULT_GROUP,"0 0/1 * * * ?");
在Job和Trigger都具備後,通過Scheduler將它們關聯起來並進行最終的任務調度
Scheduler scheduler = new StdSchedulerFactory().getScheduler();
scheduler.start();
JobDetail jobDetail = new JobDetail("jobName",Scheduler.DEFAULT_GROUP,FXNewsJob.class);
Trigger cronTrigger = new CronTrigger("cronTriggerName",Scheduler.DEFAULT_GROUP,"0 0/1 * * * ?");
scheduler.scheduleJob(jobDetail,cronTrigger);
4.融入Spring大家庭的Quartz
1)Job的實現策略
在Quartz中,每一Job所需要的執行上下文(Execution Context)信息是由其對應的JobDetail提供的,二者通過JobDataMap進行數據通信
JobDetail jobDetail = new JobDetail("jobName",Scheduler.DEFAULT_GROUP,HelloWorldJob.class);
jobDetail.getJobDataMap().put("message","helloworld");
jobDetail.getJobDataMap().put("counter",10);
public class HelloWorldJob implements Job{
public void execute(JobExecutionContext ctx) throws JobExecutionException{
JobDataMap jobDataMap = ctx.getJobDetail().getJobDataMap();
String message = jobDataMap.getString("message");
int counter = jobDataMap.getInt("counter");
//...
}
}
Spring提供了org.springframework.scheduling.quartz.QuartzJobBean。在實現Job的時候,通過繼承QuartzJobBean,讓我們在Job實現類中,直接以bean屬性的形式訪問當前Job執行上下文信息
public class HelloWorldJobExtendingQuartzJobBean extends QuartzJobBean{
private String message;
private int counter;
//getXXX();
//setXXX();
public void executeInternal(JobExecutionContext ctx) throws JobExecutionException{
//getMessage()並使用
//getCounter()並使用
}
}
Spring提供了org.springframework.scheduling.quartz.SpringBeanJobFactory是一種更好的選擇,允許對要執行的Job進行定製
2)JobDetail的更多選擇
通過org.springframework.scheduling.quartz.JobDetailBean來創建和配置相應Job所對應的JobDetail實例
<bean>
<property name="jobClass" value="HelloWorldJob | HelloWorldJobExtendingQuartzJobBean" />
<property name="jobDataAsMap">
<map>
<entry key="message">
<value>HelloWorld</value>
</entry>
<entry key="counter">
<value>10</value>
</entry>
</map>
</property>
</bean>
一個比較優雅的MethodInvokingJobDetailFactoryBean
<bean>
<property name="targetObject" ref="fxNewsProvider" />
<property name="targetMethod" value="getAndPersistNews" />
</bean>
MethodInvokingJobDetailFactoryBean會在內部構建相應的Job實現類(MethodInvokingJob和StatefulMethodInvokingJob),這些Job實現類會在合適的時機調用指定給MethodInvokingJobDetailFactoryBean的業務對象上的業務方法。但是,通過它所返回的JobDetail信息是不可序列化的,從而也就無法保存或者說持久化。
3)Trigger的可配置化
Spring分別提供了SimpleTriggerBean和CronTrggerBean封裝類,同樣採用FactoryBean機制實現
<bean>
<property name="jobDetail" ref="jobDetail"/>
<property name="repeatInterval" value="3000"/>
</bean>
<bean>
<property name="jobDetail" ref="jobDetail"/>
<property name="cronExpression" value="0 0/1 * * * ?"/>
</bean>
可以結合Trgger與Quartz提供的Calender來達成最終所需要的調度規則。Trigger負責提供覆蓋範圍足夠廣的調度規則,而Calender負責排除這一規則範圍內不需要的部分
4)Scheduler的新家
Spring提供了org.springframework.scheduling.quartz.SchedulerFactoryBean對Quartz的Scheduler進行管理
<bean>
<property name="triggers">
<ref bean="newsTrigger" />
<ref bean="simpleTrigger" />
</property>
</bean>
FXNewsProvider相關任務調度類配置代碼示例
<bean p:newsListener-ref="newsListener" p:newsListener-ref="newsPersister"/>
<bean>
<property name="targetObject" ref="fxNewsProvider" />
<property name="targetMethod" value="getAndPersistNews" />
</bean>
<bean>
<property name="jobDetail" ref="jobDetail" />
<property name="cronExpression" value="0 0/1 * * * ?" />
</bean>
<bean>
<property name="triggers">
<list>
<ref bean="newsTrigger"/>
</list>
</property>
</bean>
5.Spring對JDK Timer的集成
JDK Timer小記:所有經由Timer進行調度的任務都需要繼承TimerTask
public class FXNewsTimerTask extends TimerTask{
private FXNewsProvider fxNewsProvider;
//getXXX();
//setXXX();
public void run(){
getFxNewsProvider().getAndPersistNews();
}
}
FXNewsTimerTask task = new FXNewsTimerTask();
Timer timer = new Timer("schedulerName[optional]");
timer.schedule(task,0,60000);
1)逃離TimerTask的魔咒
<bean p:newListener-ref="newsListener" p:newPersistencer-ref="newsPersister">
<bean>
<property name="targetObject" ref="fxNewsProvider"/>
<property name="targetMethod" value="getAndPersistNews"/>
</bean>
2)TimerTask的模塊化封裝-ScheduledTimerTask
通過一個ScheduledTimerTask將對應的TimerTask和相關的Trigger信息封裝到一個可以統一管理的實體中
<bean>
<property name="timerTask" ref="task" />
<property name="period" value="3000">
</bean>
3)Timer的新家-TimerFactoryBean
TimerFactoryBean實現代碼摘錄
ScheduledTimerTask[] stt = ... ;
for(ScheduledTimerTask task : stt){
if(task.isOneTimeTask()){
timer.schedule(task.getTimerTask(),task.getDelay());
}else{
if(task.isFixedRate()){
timer.scheduleAtFixedRate(task.getTimerTask(),task.getDelay(),task.getPeriod());
}else{
timer.schedule(task.getTimerTask(),task.getDelay(),task.getPeriod());
}
}
}
<bean>
<property name="scheduledTimerTasks">
<list>
<ref bean="scheduledTask" />
</list>
</property>
</bean>
6.Executor的孿生兄弟TaskExecutor
Java5爲我們帶來了一套新的任務執行框架,以Executor爲首:
public interface Executor{
void execute(Runnable task);
}
Executor這一抽象的意義在於,可以將任務的提交(task commission)和任務的執行(task execution)策略分隔開來,解除二者之間的耦合性,以Runnable類型界定的任務提交之後,最終會以什麼樣的策略執行(什麼時間執行,交給誰執行,等等),完全由不同的Executor實現類負責,提交任務的客戶端完全可以忽略後者的實現細節。
Spring提供的TaskExecutor完成Executor同樣的功能
public interface TaskExecutor{
void execute(Runnable task);
}
7.可用的TaskExecutor
1)SyncTaskExecutor
提交給SyncTaskExecutor的任務將直接在當前線程中執行
public class SyncTaskExecutor implements TaskExecutor,Serializable{
public void execute(Runnable task){
Assert.notNull(task,"Runnable must not be null");
task.run();
}
}
2)SimpleAsyncTaskExecutor
提供最基本的異步執行能力,實現的方式則是爲每個任務都創建新的線程
public class SimpleAsyncTaskExecutor implements TaskExecutor{
public void execute(Runnable task){
new Thread(task).start();
}
}
SimpleAsyncTaskExecutor提供了相應的屬性以控制創建的線程數目上限
<bean>
<property name="concurrencyLimit" value="100" />
</bean>
3)ThreadPoolTaskExecutor
用線程池來管理並重用處理任務異步執行的工作線程,ThreadPoolTaskExecutor是對標準的java.util.concurrent.ThreadPoolExecutor進行了封裝
Spring提供的ThreadPoolTaskExecutor一共有兩個:
org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor是對java.util.concurrent.ThreadPoolExecutor進行了封裝
org.springframework.scheduling.backportconcurrent.ThreadPoolTaskExecutor是對JSR-166 backport的ThreadPoolTaskExecutor進行封裝
<bean>
<property name="corePoolSize" value="10">
<property name="maxPoolSize" value="20">
<property name="queueCapacity" value="20">
</bean>
4)ConcurrentTaskExecutor
爲Java5的Executor和Spring的TaskExecutor搭建了一道橋樑。構建自己需要的Executor實例,通過Executor.newXXXThreadPool(),然後以ConcurrentTaskExecutor對其進行封裝,封裝後獲得的ConcurrentTaskExecutor即獲得響應Executor的能力。
Executor executor = Executors.newScheduledThreadPool(10);
TaskExecutor taskExecutor = new ConcurrentTaskExecutor(executor);
5)TimeTaskExecutor、SimpleThreadPoolTaskExecutor和WorkManagerTaskExecutor
都是使用特定的調度程序(Job Scheduler)來執行提交給他們的任務
public class PrototypeTimerTaskExecutor implements TaskExecutor{
private Timer timer = new Timer();
public void execute(final Runnable task){
timer.schedule(new TimerTask(){ public void run(){task.run();}},new Date());
}
}