wangmengzhao
2023-12-08 0f550cb81c4d663849a54e7f27b26c737dc47ec7
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
package com.ruoyi.quartz.util;
 
import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.Job;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.TriggerBuilder;
import org.quartz.TriggerKey;
import com.ruoyi.common.constant.ScheduleConstants;
import com.ruoyi.common.exception.job.TaskException;
import com.ruoyi.common.exception.job.TaskException.Code;
import com.ruoyi.quartz.domain.SysJob;
 
/**
 * 定时任务工具类
 *
 * @author ruoyi
 */
public class ScheduleUtils {
 
    /**
     * Quartz API的关键接口是:
     * Scheduler - 与调度程序交互的主要API
     * Job - 由希望由调度程序执行的组件实现的接口
     * JobDetail - 用于定义作业的实例
     * Trigger(即触发器) - 定义执行给定作业的计划的组件
     * JobBuilder - 用于定义/构建JobDetail实例,用于定义作业的实例
     * TriggerBuilder - 用于定义/构建触发器实例
     */
 
 
    /**
     * 得到quartz任务类
     *
     * @param sysJob 执行计划
     * @return 具体执行任务类
     */
    private static Class<? extends Job> getQuartzJobClass(SysJob sysJob) {
        // 查看任务是否允许并发执行
        boolean isConcurrent = "0".equals(sysJob.getConcurrent());
        // 根据是否允许并发分别得到对应的class
        return isConcurrent ? QuartzJobExecution.class : QuartzDisallowConcurrentExecution.class;
    }
 
    /**
     * 构建任务触发对象
     * 进行字符串拼接(拼接前缀)
     */
    public static TriggerKey getTriggerKey(Long jobId, String jobGroup) {
        return TriggerKey.triggerKey(ScheduleConstants.TASK_CLASS_NAME + jobId, jobGroup);
    }
 
    /**
     * 构建任务键对象
     * 进行字符串拼接(拼接前缀)
     */
    public static JobKey getJobKey(Long jobId, String jobGroup) {
        return JobKey.jobKey(ScheduleConstants.TASK_CLASS_NAME + jobId, jobGroup);
    }
 
    /**
     * 创建定时任务
     */
    public static void createScheduleJob(Scheduler scheduler, SysJob job) throws SchedulerException, TaskException {
        //得到quartz任务类的Class
        //quartz任务类继承了AbstractQuartzJob类,而该类实现了Job接口
        Class<? extends Job> jobClass = getQuartzJobClass(job);
        // 构建job信息
        //获取任务id
        Long jobId = job.getJobId();
        //创建Job对象,获取任务组名
        String jobGroup = job.getJobGroup();
        //使用JobBuolder根据quartz任务类的class使用静态方法newJob去build一个JobDetail的实例
        // 这个实例就可以去执行quartz任务类的相关方法(定义一个Job作业)
        // withIdentity设置后,就可以在使用时使用其name和group作为Trigger去触发
        JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(getJobKey(jobId, jobGroup)).build();
 
        // 表达式调度构建器,作为任务调度的容器
        // 使用cron表达式
        // 也使用了建造者模式
        // 将sys_job表中任务所携带的cron表达式字段赋给Builder
        CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression());
        //设置定时任务策略(cron计划策略)
        cronScheduleBuilder = handleCronScheduleMisfirePolicy(job, cronScheduleBuilder);
 
        // 按新的cronExpression表达式构建一个新的trigger触发器
        // 使用withIdentity与上方的Job进行绑定
        CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(getTriggerKey(jobId, jobGroup))
                .withSchedule(cronScheduleBuilder).build();
 
        // 放入参数,运行时的方法可以获取
        jobDetail.getJobDataMap().put(ScheduleConstants.TASK_PROPERTIES, job);
 
        // 判断当前scheduler容器中是否存在该任务
        if (scheduler.checkExists(getJobKey(jobId, jobGroup))) {
            // 防止创建时存在数据问题 先移除,然后在执行创建操作
            scheduler.deleteJob(getJobKey(jobId, jobGroup));
        }
        //使用触发器执行定时任务
        scheduler.scheduleJob(jobDetail, trigger);
 
        // 暂停任务
        if (job.getStatus().equals(ScheduleConstants.Status.PAUSE.getValue())) {
            scheduler.pauseJob(ScheduleUtils.getJobKey(jobId, jobGroup));
        }
    }
 
    /**
     * 设置定时任务策略
     */
    public static CronScheduleBuilder handleCronScheduleMisfirePolicy(SysJob job, CronScheduleBuilder cb)
            throws TaskException {
        //判断任务的执行策略(cron计划策略)
        // 具体请查看ScheduleConstants
        switch (job.getMisfirePolicy()) {
            // 默认执行策略
            case ScheduleConstants.MISFIRE_DEFAULT:
                return cb;
            //立即触发执行
            case ScheduleConstants.MISFIRE_IGNORE_MISFIRES:
                return cb.withMisfireHandlingInstructionIgnoreMisfires();
            //触发执行一次
            case ScheduleConstants.MISFIRE_FIRE_AND_PROCEED:
                return cb.withMisfireHandlingInstructionFireAndProceed();
            //不触发立即执行
            case ScheduleConstants.MISFIRE_DO_NOTHING:
                return cb.withMisfireHandlingInstructionDoNothing();
            //立即触发执行
            default:
                throw new TaskException("The task misfire policy '" + job.getMisfirePolicy()
                        + "' cannot be used in cron schedule tasks", Code.CONFIG_ERROR);
        }
    }
}