quartz学习笔记之小试牛刀(一)

本文内容

本文记录从数据库中加载定时任务的配置并运行的过程。

数据结构

官方提供的表结构:

Quartz官方定时器相关表结构-201962322275

Maven中使用

添加依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
<quartz.version>2.2.3</quartz.version>
<!--Quartz定时器的依赖-->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>${quartz.version}</version>
</dependency>
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz-jobs</artifactId>
<version>${quartz.version}</version>
</dependency>
<!--Quartz定时器的依赖-->

Spring中配置方式

使用@Scheduled 注解;

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
```java
package com.weyoung.platform.quartz.schedule;

import com.weyoung.framework.annotation.QuartzLog;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

/**
* @Author Mr.wang
* @Description
* @Date 2019/4/29
*/
@Component
public class MainSchedule {

private static final Logger logger = LoggerFactory.getLogger(MainSchedule.class);

public MainSchedule() {
logger.info("-------------------------定时器MainSchedule创建成功-------------------------");
}

@Scheduled(cron = "0/1 * * * * ?")
@QuartzLog
public static void run() {
logger.info("-------------------------定时器调度start-------------------------");
}
}

使用SchedulingConfigurer配置定时任务,使用工具类

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

#### 测试表数据

```sql

-- qrtz_job_details 表数据
INSERT INTO `qrtz_job_details` (
`SCHED_NAME`,
`JOB_NAME`,
`JOB_GROUP`,
`DESCRIPTION`,
`JOB_CLASS_NAME`,
`IS_DURABLE`,
`IS_NONCONCURRENT`,
`IS_UPDATE_DATA`,
`REQUESTS_RECOVERY`,
`JOB_DATA`
)
VALUES
(
'quartzScheduler',
'1',
'1',
'测试任务',
'com.weyoung.platform.quartz.job.TestJob2.execute',
'1',
'0',
'0',
'0',
NULL
) ;

INSERT INTO `qrtz_job_details` (
`SCHED_NAME`,
`JOB_NAME`,
`JOB_GROUP`,
`DESCRIPTION`,
`JOB_CLASS_NAME`,
`IS_DURABLE`,
`IS_NONCONCURRENT`,
`IS_UPDATE_DATA`,
`REQUESTS_RECOVERY`,
`JOB_DATA`
)
VALUES
(
'quartzScheduler',
'2',
'1',
'TestJob',
'com.weyoung.platform.quartz.job.TestJob.execute',
'1',
'0',
'0',
'0',
NULL
) ;
-- qrtz_triggers 触发器信息表数据
INSERT INTO `qrtz_triggers` (
`SCHED_NAME`,
`TRIGGER_NAME`,
`TRIGGER_GROUP`,
`JOB_NAME`,
`JOB_GROUP`,
`DESCRIPTION`,
`NEXT_FIRE_TIME`,
`PREV_FIRE_TIME`,
`PRIORITY`,
`TRIGGER_STATE`,
`TRIGGER_TYPE`,
`START_TIME`,
`END_TIME`,
`CALENDAR_NAME`,
`MISFIRE_INSTR`,
`JOB_DATA`
)
VALUES
(
'quartzScheduler',
'1',
'1',
'1',
'1',
'触发器信息',
'1561338500',
'1561338500',
'1',
'1',
'1',
'1561338500',
NULL,
NULL,
NULL,
NULL
) ;

INSERT INTO `qrtz_triggers` (
`SCHED_NAME`,
`TRIGGER_NAME`,
`TRIGGER_GROUP`,
`JOB_NAME`,
`JOB_GROUP`,
`DESCRIPTION`,
`NEXT_FIRE_TIME`,
`PREV_FIRE_TIME`,
`PRIORITY`,
`TRIGGER_STATE`,
`TRIGGER_TYPE`,
`START_TIME`,
`END_TIME`,
`CALENDAR_NAME`,
`MISFIRE_INSTR`,
`JOB_DATA`
)
VALUES
(
'quartzScheduler',
'2',
'1',
'2',
'1',
'触发器信息',
'1561338500',
'1561338500',
'1',
'1',
'1',
'1561338500',
NULL,
NULL,
NULL,
NULL
) ;
-- qrtz_cron_triggers 触发器的CRON表达式信息
INSERT INTO `qrtz_cron_triggers` (
`SCHED_NAME`,
`TRIGGER_NAME`,
`TRIGGER_GROUP`,
`CRON_EXPRESSION`,
`TIME_ZONE_ID`
)
VALUES
(
'quartzScheduler',
'1',
'1',
'* * * * * ? *',
'Asia/Shanghai'
) ;

INSERT INTO `qrtz_cron_triggers` (
`SCHED_NAME`,
`TRIGGER_NAME`,
`TRIGGER_GROUP`,
`CRON_EXPRESSION`,
`TIME_ZONE_ID`
)
VALUES
(
'quartzScheduler',
'2',
'1',
'3 * * * * ? *',
'Asia/Shanghai'
) ;

QuartzUtil.java 定时任务工具类

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
204
205
206
207
package com.weyoung.platform.quartz.utils;

import com.weyoung.framework.exception.BusinessException;
import org.apache.commons.lang3.StringUtils;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import org.springframework.stereotype.Component;

import static com.weyoung.framework.common.Constants.SPLIT_DOT;

/**
* @Author Mr.wang
* @Description 定时任务管理器
* @Date 2019/5/9
*/
@Component("quartzUtil")
public class QuartzUtil {

private static Scheduler scheduler;

static {
try {
scheduler = new StdSchedulerFactory().getScheduler();
} catch (SchedulerException e) {
throw new BusinessException("获取调度器实例异常!", e);
}
}
private static String JOB_GROUP_NAME = "LX_JOB_GROUP_NAME";
private static String TRIGGER_GROUP_NAME = "LX_TRIGGER_GROUP_NAME";

public static void setScheduler(Scheduler scheduler) {
QuartzUtil.scheduler = scheduler;
}

/**
* 添加一个定时任务,使用默认的任务组名,触发器名,触发器组名
*
* @param jobClass Job任务类实例
* @param jobName job名字(请保证唯一性)
* @param cronExpression cron时间表达式
* @throws SchedulerException
*/
public void addJob(String jobName, Class jobClass, String cronExpression)
throws SchedulerException {
addJob(jobClass, jobName, null, jobName, null, cronExpression, 5);
}

/**
* 开始一个simpleSchedule()调度(创建一个新的定时任务)
*
* @param jobClass Job任务类实例
* @param jobName job名字(请保证唯一性)
* @param jobGroupName job组名(请保证唯一性)
* @param cronExpression cron时间表达式
* @param triggerName trigger名字(请保证唯一性)
* @param triggerGroupName triggerJob组名(请保证唯一性)
* @param triggerPriority trigger优先级
* @throws SchedulerException
*/
public void addJob(Class jobClass, String jobName, String jobGroupName, String triggerName, String triggerGroupName,
String cronExpression, int triggerPriority) throws SchedulerException {

if (StringUtils.isEmpty(jobGroupName)) {
jobGroupName = JOB_GROUP_NAME;
}
if (StringUtils.isEmpty(triggerGroupName)) {
triggerGroupName = TRIGGER_GROUP_NAME;
}
// 1、创建一个JobDetail实例,指定Quartz
JobDetail jobDetail = JobBuilder
// 任务执行类
.newJob(jobClass)
// 任务名,任务组
.withIdentity(jobName, jobGroupName)
.build();
// 2、创建Trigger
// 优先级默认是5,数字越高优先级越高
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity(triggerName, triggerGroupName).startNow()
.withSchedule(CronScheduleBuilder.cronSchedule(cronExpression))
.withPriority(triggerPriority)
.build();
try {
// 判断调度其中是否存在该任务,不存在时添加,否则会出现错误
if (!scheduler.checkExists(JobKey.jobKey(jobName, jobGroupName))) {
scheduler.scheduleJob(jobDetail, trigger);
}
} catch (SchedulerException e) {
throw new BusinessException("添加任务[" + jobName + "]到调度器中出现异常!", e);
}
// 4、启动
this.startSchedule();
}

/**
* 开始任务
*/
public void startSchedule() {
try {
if (scheduler.isShutdown()) {
// 恢复
scheduler.resumeAll();
} else {
// 启动
scheduler.start();
}
} catch (SchedulerException e) {
throw new BusinessException("开启任务调度器中出现异常!", e);
}
}

/**
* 暂停Job
*
* @param jobName job名字
* @param jobGroupName job组名
*/
public void pauseJob(String jobName, String jobGroupName) {
JobKey jobKey = JobKey.jobKey(jobName, jobGroupName);
try {
scheduler.pauseJob(jobKey);
} catch (SchedulerException e) {
throw new BusinessException("暂停任务[" + jobGroupName + SPLIT_DOT + jobName + "]中出现异常!", e);
}
}

/**
* 恢复Job
*
* @param jobName job名字
* @param jobGroupName job组名
*/
public void resumeJob(String jobName, String jobGroupName) {
JobKey jobKey = JobKey.jobKey(jobName, jobGroupName);
try {
scheduler.resumeJob(jobKey);
} catch (SchedulerException e) {
throw new BusinessException("恢复任务[" + jobGroupName + SPLIT_DOT + jobName + "]中出现异常!", e);
}
}

/**
* 修改一个任务的触发时间(使用默认的任务组名,触发器名,触发器组名)
*/
public void modifyJobTime(String jobName, String cronExpression)
throws SchedulerException {
rescheduleJob(jobName, null, cronExpression);
}

/**
* 更新任务表达式
*
* @param triggerName trigger名字
* @param triggerGroupName trigger组名
* @param newCronExpression cron时间表达式
* @throws SchedulerException
*/
public void rescheduleJob(String triggerName, String triggerGroupName, String newCronExpression) throws SchedulerException {
if (StringUtils.isBlank(triggerGroupName)) {
triggerGroupName = TRIGGER_GROUP_NAME;
}
TriggerKey triggerKey = TriggerKey.triggerKey(triggerName, triggerGroupName);
// 获取trigger
CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
// 按新的cronExpression表达式重新构建trigger
trigger = trigger
.getTriggerBuilder()
.withIdentity(triggerKey)
.withSchedule(CronScheduleBuilder.cronSchedule(newCronExpression))
.build();
// 按新的trigger重新设置job执行
try {
scheduler.rescheduleJob(triggerKey, trigger);
} catch (SchedulerException e) {
throw new BusinessException("更新任务调度策略出现异常!", e);
}
}

/**
* 移除一个任务和触发器(使用默认的任务组名,触发器名,触发器组名)
*/
public void removeJob(String jobName, String triggerName) throws SchedulerException {
this.removeJob(jobName, null, triggerName, null);
}

/**
* 移除一个任务和触发器
*/
public void removeJob(String jobName, String jobGroupName, String triggerName, String triggerGroupName)
throws SchedulerException {
if (StringUtils.isEmpty(jobGroupName)) {
jobGroupName = JOB_GROUP_NAME;
}
if (StringUtils.isEmpty(triggerGroupName)) {
triggerGroupName = TRIGGER_GROUP_NAME;
}
JobKey jobKey = JobKey.jobKey(jobName, jobGroupName);
TriggerKey triggerKey = TriggerKey.triggerKey(triggerName, triggerGroupName);
// 停止触发器
scheduler.pauseTrigger(triggerKey);
// 移除触发器
scheduler.unscheduleJob(triggerKey);
// 删除任务
scheduler.deleteJob(jobKey);
}
}

实现SchedulingConfigurer接口的类

作用是从数据库中加载定时器配置信息;
注:@PostConstruct注解使这个方法在项目启动后加载执行
LxJobConfig.java

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
package com.weyoung.platform.quartz.schedule;

import com.weyoung.framework.exception.BusinessException;
import com.weyoung.platform.quartz.dao.QuartzJobDetailsDao;
import com.weyoung.platform.quartz.pojo.QuartzJobInfo;
import com.weyoung.platform.quartz.utils.QuartzUtil;
import org.apache.commons.lang3.StringUtils;
import org.quartz.SchedulerException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;

import javax.annotation.Resource;
import java.util.List;

import static com.weyoung.framework.common.Constants.SPLIT_DOT;

/**
* @author Mr.wang
* @功能描述: 加载定时器列表
* @时间: 2019/6/23 20:24
*/
@Configuration
@EnableScheduling
public class LxJobConfig implements SchedulingConfigurer {

private static final Logger LOGGER = LoggerFactory.getLogger(LxJobConfig.class);

@Resource
private QuartzJobDetailsDao quartzJobDetailsDao;

@Resource
QuartzUtil quartzUtil;

@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
loadConfigsFromDb();
}

/**
* 从数据库中获取job配置信息并放入调度器中
*/
private void loadConfigsFromDb() {
LOGGER.info("-----------------LxJobConfig------------------loadConfigsFromDb-----------------");
// 从数据库加载Job配置信息
List<QuartzJobInfo> jobDetails = quartzJobDetailsDao.selectCronJobAndTriggerList(new QuartzJobInfo());
jobDetails.stream().forEach(config -> {
String jobClassName = config.getJobClassName();
if (jobClassName.indexOf(SPLIT_DOT) == -1) {
throw new BusinessException("定时任务配置异常,类名参数配置错误[" + jobClassName + "]!");
}
if (StringUtils.isEmpty(config.getCronExpression())) {
throw new BusinessException("定时任务配置异常,CRON表达式配置错误[" + config.getCronExpression() + "]!");
}
int splitIdx = jobClassName.lastIndexOf(".");
String className = jobClassName.substring(0, splitIdx);
try {
Class<?> jobClass = Class.forName(className);
quartzUtil.addJob(jobClass, config.getJobName(), config.getJobGroup(), config.getTriggerName(),
config.getTriggerGroup(), config.getCronExpression(), config.getPriority());
} catch (ClassNotFoundException e) {
throw new BusinessException("定时任务配置异常,类名[" + className + "]未找到!", e);
} catch (SchedulerException e) {
throw new BusinessException("添加定时任务异常!", e);
}
});
}
}

测试任务类1: TestJob.java

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
package com.weyoung.platform.quartz.job;

import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
* @功能描述:
* @时间: 2019/6/24 19:47
* @author: Mr.wang
*/
public class TestJob implements Job {

private static final Logger LOGGER = LoggerFactory.getLogger(TestJob2.class);

@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
Date date = new Date(System.currentTimeMillis());
SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String now = sf.format(date);
LOGGER.info("--------------->TestJobMain execute" + now);
}
}

测试任务类2:TestJob2

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
package com.weyoung.platform.quartz.job;

import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
* @Author Mr.wang
* @Description 定时任务实现类-测试
* @Date 2019/6/2
*/
public class TestJob2 implements Job {

private static final Logger LOGGER = LoggerFactory.getLogger(TestJob2.class);

@Override
public void execute(JobExecutionContext context) {
Date date = new Date();
SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String format = sf.format(date);
LOGGER.info("TestJob2.execute.run:{}", format);
}
}

执行结果信息

执行结果-2019624204452

只需要定时任务类实现Job接口重写其execute方法即可。

说明

上面的方式通过实现SchedulingConfigurer接口的configureTasks方法使加载数据库中定时任务的配置以及放入调度器的这一系列操作在应用启动后就开始自动执行,并使用QuartzUtil工具类将一个Job接口的实现类放入调度器中并进行调度,其核心在于QuartzUtil这个工具类。
后面的文章会将一个普通类变成一个定时调度的任务、以及通过界面管理调度器、触发器、任务等信息,包括不重启添加任务、暂停任务、恢复任务以及删除任务的方法。