在实际项目应用中经常会用到定时任务,可通过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 getPlanJobs() throws SchedulerException;
//获取所有运行中的任务
List 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 getPlanJobs() throws SchedulerException {
GroupMatcher matcher = GroupMatcher.anyJobGroup();
Set jobKeys = scheduler.getJobKeys(matcher);
List jobList = new ArrayList();
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 getRunningJobs() throws SchedulerException {
List executingJobs = scheduler.getCurrentlyExecutingJobs();
List jobList = new ArrayList();
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");
}