Quartz Job & Spring 動態任務排程
阿新 • • 發佈:2019-02-08
Quartz Job & Spring
在實際專案應用中經常會用到定時任務,可通過Quartz框架輕鬆完成。在Web專案中,如果用Spring框架管理Quartz,在Web容器啟動或關閉時自動啟動、關閉Quartz中的任務,非常方便。
傳統的MethodInvokingJobDetailFactoryBean
執行方式,配置複雜,且不夠靈活——如果要動態改變任務的狀態、cron表示式等就需要改變配置甚至程式碼需要重啟伺服器了。因此,我採取動態任務排程的方式,可自由控制任務進度,更可以顯示到html頁面上。
配置
只需要在Spring配置檔案中加上SchedulerFactoryBean。
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean" autowire="no"></bean>
這樣,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");
}