Java 第三方包-Quartz

1. wiki

Quartz 是任务调度框架,

2. 使用

2. 引用

1
2
3
4
5
6
7
8
9
<properties>
<quartz.version>2.2.1</quartz.version>
</properties>

<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>${quartz.version}</version>
</dependency>

2. 编码步骤

使用其框架,主要有几个步骤:

  • 定义 JobDetail ,使用其创建一个实现了 job 接口对象
  • 定义 Trigger ,使用 Cron 语法定时处理任务
  • 定义 Scheduler ,将 JobDetailTrigger 组装起来,启动作业

1. 定义 JobDetail

JobDetail 是一个接口,主要用来定义任务,具体的执行单元则由实现了 Job 接口的类的来定义逻辑。其中有一些概念需要记录下:

  • Job : 是一个 Quartz 接口,实现它的 execute(JobExecutionContext ctx) 方法,在其中可使用上下文对象获取 Trigger、Scheduler、JobDetail 等信息
  • JobDataMap : 其为一个 map 数据结构,用来将一些参数传递给具体作业使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 创建类并实现 Job 接口
public class HelloJob implements Job {
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
System.out.println("任务执行");
}
}

// 预定义一些参数,传递给作业
JobDataMap newJobDataMap = new JobDataMap();
newJobDataMap.put("date", LocalDateTime.now());

// 1. 定义一个工作对象 设置工作名称与组名
JobDetail job1 = JobBuilder // 充血定义
.newJob(HelloJob.class) // HelloJob 实现了 Job 接口
.withIdentity("job1", "group1") // 定义作业名和作业组
.usingJobData(newJobDataMap) // 装载预定义 JobDataMap
.usingJobData("myKey", "hello") // 也可以在使用中定义 JobDataMap
.build();
JobDetail job2 = JobBuilder // 简单定义
.newJob(HelloJob2.class)
.withIdentity("job2", "group2")
.build();

2. 定义 Trigger

多种时间间隔触发模式,可以使用 TriggerBuilder 来通用创建,或者使用相应的 Trigger 接口实现类创建

  • SimpleScheduleBuilder : 指定从一个时间开始,以一定的时间间隔,执行一定次数的任务
  • CronScheduleBuilder : 使用 cron 表达式来配置执行时间
  • CalendarIntervalScheduleBuilder : 每隔一段时间执行一次(年月日)
  • DailyTimeIntervalScheduleBuilder : 设置年月日中的某些固定日期,可以设置执行总次数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 定义一个简单触发器
Trigger trigger1 = TriggerBuilder
.newTrigger()
.withIdentity("trigger1", "group1") // 定义工作名与工作组
.startNow()
.withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(60)) // 60s 一次
.build();

// 定义一个 cron 模式调度的触发器
Trigger trigger2 = TriggerBuilder
.newTrigger()
.withIdentity("trigger2", "group2")
.withSchedule(CronScheduleBuilder.cronSchedule("* 0/3 * * * ?")) // cron 表达式
.build();

3. 定义 Scheduler

将前两者组装起来,之后启动定时器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Scheduler scheduler;
try {
// 创建定时器
scheduler = StdSchedulerFactory.getDefaultScheduler();
// 组装定时器
scheduler.scheduleJob(job1, trigger1);
scheduler.scheduleJob(job2, trigger2);
TriggerKey triggerKey = TriggerKey.triggerKey("trigger2", "group2");
if (null == triggerKey) {
System.out.println("triggerKey is null.");
} else {
System.out.println("triggerKey = " + triggerKey);
System.out.println("getTriggerGroupNames = " + scheduler.getTriggerGroupNames());

CronTrigger trigger = (CronTrigger)scheduler.getTrigger(triggerKey);
System.out.println("getCronExpression = " + trigger.getCronExpression());
System.out.println("getDescription = " + trigger.getDescription());
}
//启动定时器
scheduler.start();
} catch (SchedulerException e) {
e.printStackTrace();
}

3. 实战

在 Spring Boot 框架中使用

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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
import java.util.Objects;

import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
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 org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.common.exception.ExceptionUtil;
import com.common.exception.QuartzErrorCode;

/**
* 定时任务.
*
* @author glett
*/
@Component
public class QuartzService {
private static final Logger LOGGER = LoggerFactory.getLogger(QuartzService.class);

@Autowired
private Scheduler scheduler;

/**
* 启动所有作业.
*/
public void startJobs() {
try {
if (scheduler.isStarted()) {
LOGGER.warn("该 scheduler 已经被启动了.");
return;
}
scheduler.start();
} catch (SchedulerException e) {
handleScheExp(null, e);
}
}

/**
* 停止所有作业.
*/
public void shutdownJobs() {
try {
if (scheduler.isShutdown()) {
LOGGER.warn("该 scheduler 已经被停止了.");
return;
}
scheduler.shutdown();
} catch (SchedulerException e) {
handleScheExp(null, e);
}
}

/**
* 添加作业.
*
* @param job
*/
public void addJob(QuartzJobDomain job) {
ExceptionUtil
.checkArgument(job.verify(), QuartzErrorCode.Q_PARAM_NEC_NULL, String.format("作业 [%s] 必填参数为空.", job));
JobDetail jobDetail = createJobDetail(job);

CronTrigger trigger = createCronTrigger(job);
try {
scheduler.scheduleJob(jobDetail, trigger);
// 启动
this.startJobs();
} catch (Exception e) {
handleScheExp(job, e);
}
}

/**
* 修改作业 cron 表达式.
*
* @param job
*/
public void modifyJobTime(QuartzJobDomain job) {
ExceptionUtil.checkArgument(
job.verifyTrigger(),
QuartzErrorCode.Q_PARAM_NEC_NULL,
String.format("作业 [%s] trigger 信息必填参数为空.", job));
String cron = ExceptionUtil
.checkNonBlank(job.getCron(), QuartzErrorCode.Q_PARAM_NEC_NULL, String.format("作业 [%s] cron 信息不能为空.", job));

TriggerKey tk = getTriggerKey(job);
try {
CronTrigger oldTrigger = (CronTrigger)scheduler.getTrigger(tk);
if (oldTrigger == null) {
return;
}
String oldCron = oldTrigger.getCronExpression();
if (!oldCron.equalsIgnoreCase(cron)) {
CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(cron);
CronTrigger trigger = oldTrigger.getTriggerBuilder().withSchedule(cronScheduleBuilder).build();
// 重新设置触发器
scheduler.rescheduleJob(tk, trigger);
}
} catch (Exception e) {
handleScheExp(job, e);
}
}

/**
* 移除作业.
*
* @param job
*/
public void removeJob(QuartzJobDomain job) {
ExceptionUtil
.checkArgument(job.verifyJAndT(), QuartzErrorCode.Q_PARAM_NEC_NULL, String.format("作业 [%s] 必填参数为空.", job));

JobKey jk = getJobKey(job);
TriggerKey tk = getTriggerKey(job);
try {
scheduler.pauseTrigger(tk); // 停止触发器
scheduler.unscheduleJob(tk);// 移除触发器
scheduler.deleteJob(jk); // 删除任务
} catch (Exception e) {
handleScheExp(job, e);
}
}

/**
* 恢复作业.
*
* @param job
*/
public void resumeJob(QuartzJobDomain job) {
ExceptionUtil
.checkArgument(job.verifyJob(), QuartzErrorCode.Q_PARAM_NEC_NULL, String.format("作业 [%s] 必填参数为空.", job));

JobKey jk = getJobKey(job);
try {
scheduler.resumeJob(jk);
} catch (Exception e) {
handleScheExp(job, e);
}
}

/**
* 执行一次作业.
*
* @param job
*/
public void execOnce(QuartzJobDomain job) {
ExceptionUtil
.checkArgument(job.verifyJob(), QuartzErrorCode.Q_PARAM_NEC_NULL, String.format("作业 [%s] 必填参数为空.", job));

JobKey jk = getJobKey(job);
try {
scheduler.triggerJob(jk);
} catch (Exception e) {
handleScheExp(job, e);
}
}

/**
* 处理 scheduler 异常.
*
* @param job
* @param e
*/
private void handleScheExp(QuartzJobDomain job, Exception e) {
String msg = "scheduler 执行异常";
if (Objects.nonNull(job)) {
msg = String.format("scheduler 执行异常, 作业信息为 [jobName=%s].", job.getJobName());
}
LOGGER.error(msg, e);
throw ExceptionUtil.asExceptionUtil(QuartzErrorCode.SCHEDULER_EXCP, msg);
}

private static JobDetail createJobDetail(QuartzJobDomain job) {
return JobBuilder.newJob(job.getJobClass())
.usingJobData(job.getJobDataMap())
.withIdentity(getJobKey(job))
.build();
}

public static JobKey getJobKey(QuartzJobDomain job) {
return JobKey.jobKey(job.getJobName(), job.getJobGroupName());
}

private static CronTrigger createCronTrigger(QuartzJobDomain job) {
CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCron());
CronTrigger trigger =
TriggerBuilder.newTrigger().withIdentity(getTriggerKey(job)).withSchedule(cronScheduleBuilder).build();
return trigger;
}

public static TriggerKey getTriggerKey(QuartzJobDomain job) {
return TriggerKey.triggerKey(job.getTriggerName(), job.getTriggerGroupName());
}
}