Quartz Job & Spring
在實際項目應用中經常會用到定時任務,可通過Quartz架構輕松完成。在Web項目中,如果用Spring架構管理Quartz,在Web容器啟動或關閉時自動啟動、關閉Quartz中的任務,非常友善。
傳統的
MethodInvokingJobDetailFactoryBean
運作方式,配置複雜,且不夠靈活——如果要動态改變任務的狀态、cron表達式等就需要改變配置甚至代碼需要重新開機伺服器了。是以,我采取動态任務排程的方式,可自由控制任務進度,更可以顯示到html頁面上。

配置
隻需要在Spring配置檔案中加上SchedulerFactoryBean。
這樣,Spring就為我們建立了一個空的Scheduler,我們後面手動添加任務進去。
任務
我們定義一個Job類,任務都在這個Job類上執行
public class QuartzJobFactory implements Job {
public void execute(JobExecutionContext context)
throws JobExecutionException{
System.out.println("任務執行了");
}
}
既然記錄任務狀态,那就需要定義一個類了,我們定義一個
ScheduleJob
,
public class ScheduleJobDomain {
private String jobId; //任務id
private String jobName; //任務名稱
/** 任務狀态 */
private String jobStatus;
private String quartz; //cron表達式
//省略get、set方法
}
再定義一個Service
/**
* 管理quartz任務的service
*/
public interface ScheduleJobService {
//擷取所有計劃中的任務
List<ScheduleJobDomain> getPlanJobs() throws SchedulerException;
//擷取所有運作中的任務
List<ScheduleJobDomain> getRunningJobs() throws SchedulerException;
//暫停任務
void pauseJob(ScheduleJobDomain scheduleJob) throws SchedulerException;
//恢複任務
void resumeJob(ScheduleJobDomain scheduleJob) throws SchedulerException;
//删除任務
void deleteJob(ScheduleJobDomain scheduleJob) throws SchedulerException;
//立即運作任務 ,隻會運作一次
void runOnce(ScheduleJobDomain scheduleJob) throws SchedulerException;
//更新任務的時間表達式
void updateExpression(ScheduleJobDomain job,String expression) throws SchedulerException;
//添加任務
void addJob(ScheduleJobDomain scheduleJob) throws SchedulerException;
}
Service的實作類如下
@Service
public class ScheduleJobServiceImpl implements ScheduleJobService {
@Autowired
private Scheduler scheduler;
@Override
public List<ScheduleJobDomain> getPlanJobs() throws SchedulerException {
GroupMatcher<JobKey> matcher = GroupMatcher.anyJobGroup();
Set<JobKey> jobKeys = scheduler.getJobKeys(matcher);
List<ScheduleJobDomain> jobList = new ArrayList<ScheduleJobDomain>();
for(JobKey jobKey : jobKeys){
List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey);
for (Trigger trigger : triggers){
ScheduleJobDomain job = (ScheduleJobDomain) trigger.getJobDataMap().get("scheduleJob");
Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
job.setJobStatus(triggerState.name());
if (trigger instanceof CronTrigger) {
CronTrigger cronTrigger = (CronTrigger) trigger;
String cronExpression = cronTrigger.getCronExpression();
job.setQuartz(cronExpression);
}
jobList.add(job);
}
}
return jobList;
}
@Override
public List<ScheduleJobDomain> getRunningJobs() throws SchedulerException {
List<JobExecutionContext> executingJobs = scheduler.getCurrentlyExecutingJobs();
List<ScheduleJobDomain> jobList = new ArrayList<ScheduleJobDomain>();
for (JobExecutionContext executingJob : executingJobs){
Trigger trigger = executingJob.getTrigger();
ScheduleJobDomain job = (ScheduleJobDomain) trigger.getJobDataMap().get("scheduleJob");
Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
job.setJobStatus(triggerState.name());
if (trigger instanceof CronTrigger) {
CronTrigger cronTrigger = (CronTrigger) trigger;
String cronExpression = cronTrigger.getCronExpression();
job.setQuartz(cronExpression);
}
jobList.add(job);
}
return jobList;
}
@Override
public void pauseJob(ScheduleJobDomain scheduleJob) throws SchedulerException {
JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
scheduler.pauseJob(jobKey);
}
@Override
public void resumeJob(ScheduleJobDomain scheduleJob) throws SchedulerException {
JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
scheduler.resumeJob(jobKey);
}
@Override
public void deleteJob(ScheduleJobDomain scheduleJob) throws SchedulerException {
JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
scheduler.deleteJob(jobKey);
}
@Override
public void runOnce(ScheduleJobDomain scheduleJob) throws SchedulerException {
JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
scheduler.triggerJob(jobKey);
}
@Override
public void updateExpression(ScheduleJobDomain scheduleJob, String expression) throws SchedulerException {
TriggerKey triggerKey = TriggerKey.triggerKey(scheduleJob.getJobName(),
scheduleJob.getJobGroup());
CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(expression);
trigger = trigger.getTriggerBuilder().withIdentity(triggerKey)
.withSchedule(scheduleBuilder).build();
scheduler.rescheduleJob(triggerKey, trigger);
}
@Override
public void addJob(ScheduleJobDomain scheduleJob) throws SchedulerException {
TriggerKey key = TriggerKey.triggerKey(scheduleJob.getJobName(),scheduleJob.getJobGroup());
Trigger trigger = scheduler.getTrigger(key);
if(trigger == null){
//在建立任務時如果不存在建立一個
JobDetail jobDetail = JobBuilder.newJob(QuartzJobFactory.class)
.withIdentity(scheduleJob.getJobName(),scheduleJob.getJobGroup()).build();
jobDetail.getJobDataMap().put("scheduleJob",scheduleJob);
//表達式排程建構器
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob.getQuartz());
//按新的cronExpression表達式建構一個新的trigger
trigger = TriggerBuilder.newTrigger().withIdentity(scheduleJob.getJobName(),scheduleJob.getJobGroup())
.withSchedule(scheduleBuilder).build();
trigger.getJobDataMap().put("scheduleJob",scheduleJob);
scheduler.scheduleJob(jobDetail,trigger);
}else{
// Trigger已存在,那麼更新相應的定時設定
//表達式排程建構器
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob.getQuartz());
trigger = TriggerBuilder.newTrigger().withIdentity(key).withSchedule(scheduleBuilder).build();
trigger.getJobDataMap().put("scheduleJob",scheduleJob);
//重新執行
scheduler.rescheduleJob(key,trigger);
}
}
這樣,我們在頁面Controller中,就可以直接使用BankJobService來管理任務的狀态了。
附
在Job中如何得到Spring的Bean
由于Job對象是Quartz建立的,它沒有被注冊到Spring容器中,是以無法直接通過@Autowired得到Spring的Bean對象,解決方式是從BankJobServiceImpl中拿到需要的SpringBean,然後在addJob時放到JobDetail裡面去。
比如拿到Mybatis的SqlSessionTemplate。
public class BankJobServiceImpl{
@Autowired
private SqlSessionTemplate sqlSessionTemplate;
}
在addJob方法裡面, 修改
JobDetail jobDetail = JobBuilder.newJob(QuartzJobFactory.class)
.withIdentity(scheduleJob.getJobName(),scheduleJob.getJobGroup()).build();
jobDetail.getJobDataMap().put("sqlSessionTemplate",sqlSessionTemplate);
然後在QuartzJobFactory中
public void execute(JobExecutionContext context) throws JobExecutionException{
SqlSessionTemplate sqlSessionTemplate = (SqlSessionTemplate) context.getMergedJobDataMap().get("sqlSessionTemplate");
}