Quartz Job & Spring 动态任务调度

Quartz Job & Spring

在实际项目应用中经常会用到定时任务,可通过Quartz框架轻松完成。在Web项目中,如果用Spring框架管理Quartz,在Web容器启动或关闭时自动启动、关闭Quartz中的任务,非常方便。

传统的MethodInvokingJobDetailFactoryBean运行方式,配置复杂,且不够灵活——如果要动态改变任务的状态、cron表达式等就需要改变配置甚至代码需要重启服务器了。因此,我采取动态任务调度的方式,可自由控制任务进度,更可以显示到html页面上。

Quartz Job & Spring 动态任务调度_第1张图片

配置

只需要在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 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");
}

你可能感兴趣的:(spring,quartz,springmvc)