天天看點

Quartz Job & Spring 動态任務排程Quartz Job & Spring配置任務附

Quartz Job & Spring

在實際項目應用中經常會用到定時任務,可通過Quartz架構輕松完成。在Web項目中,如果用Spring架構管理Quartz,在Web容器啟動或關閉時自動啟動、關閉Quartz中的任務,非常友善。

傳統的

MethodInvokingJobDetailFactoryBean

運作方式,配置複雜,且不夠靈活——如果要動态改變任務的狀态、cron表達式等就需要改變配置甚至代碼需要重新開機伺服器了。是以,我采取動态任務排程的方式,可自由控制任務進度,更可以顯示到html頁面上。

Quartz Job & Spring 動态任務排程Quartz Job & Spring配置任務附

配置

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