SpringBoot+Quartz图形化(有源码) 在我们日

在我们日常的开发中,很多时候,定时任务都不是写死的,都是写好代码,页面配置,定时器执行规则都是灵活的,是写到数据库中,从而实现定时任务的动态配置,下面就通过一个简单的示例,来实现这个功能。

github.com/huyufan1995…

本次demo是SpringBoot的,毕竟是19年了,废话不多说了。

1.maven引入的依赖

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
html复制代码<dependencies>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.0.0</version>
</dependency>

</dependencies>

2.application.yml配置文件

1
2
3
4
5
6
7
8
9
10
11
html复制代码spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/quartz_test?useSSL=false&useUnicode=true
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver

# Mybatis����
mybatis:
mapperLocations: classpath:mapper/*.xml
configLocation: classpath:mybatis.xml

3.整合quartz官方推荐要写一个配置文件quartz.properties

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
html复制代码# 固定前缀org.quartz
# 主要分为scheduler、threadPool、jobStore、plugin等部分
#
#
org.quartz.scheduler.instanceName = DefaultQuartzScheduler
org.quartz.scheduler.rmi.export = false
org.quartz.scheduler.rmi.proxy = false
org.quartz.scheduler.wrapJobExecutionInUserTransaction = false

# 实例化ThreadPool时,使用的线程类为SimpleThreadPool
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool

# threadCount和threadPriority将以setter的形式注入ThreadPool实例
# 并发个数
org.quartz.threadPool.threadCount = 5
# 优先级
org.quartz.threadPool.threadPriority = 5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true

org.quartz.jobStore.misfireThreshold = 5000

# 默认存储在内存中
#org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore

#持久化
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX

org.quartz.jobStore.tablePrefix = QRTZ_

org.quartz.jobStore.dataSource = qzDS

org.quartz.dataSource.qzDS.driver = com.mysql.jdbc.Driver

org.quartz.dataSource.qzDS.URL = jdbc:mysql://127.0.0.1:3306/quartz_test?useSSL=false&useUnicode=true&characterEncoding=UTF-8

org.quartz.dataSource.qzDS.user = root

org.quartz.dataSource.qzDS.password = 123456

org.quartz.dataSource.qzDS.maxConnections = 10

4.mysql需要的quartz官方表支持

DROP TABLE IF EXISTS QRTZ_FIRED_TRIGGERS;

DROP TABLE IF EXISTS QRTZ_PAUSED_TRIGGER_GRPS;

DROP TABLE IF EXISTS QRTZ_SCHEDULER_STATE;

DROP TABLE IF EXISTS QRTZ_LOCKS;

DROP TABLE IF EXISTS QRTZ_SIMPLE_TRIGGERS;

DROP TABLE IF EXISTS QRTZ_SIMPROP_TRIGGERS;

DROP TABLE IF EXISTS QRTZ_CRON_TRIGGERS;

DROP TABLE IF EXISTS QRTZ_BLOB_TRIGGERS;

DROP TABLE IF EXISTS QRTZ_TRIGGERS;

DROP TABLE IF EXISTS QRTZ_JOB_DETAILS;

DROP TABLE IF EXISTS QRTZ_CALENDARS;

CREATE TABLE QRTZ_JOB_DETAILS

(

SCHED_NAME VARCHAR(120) NOT NULL,

JOB_NAME VARCHAR(200) NOT NULL,

JOB_GROUP VARCHAR(200) NOT NULL,

DESCRIPTION VARCHAR(250) NULL,

JOB_CLASS_NAME VARCHAR(250) NOT NULL,

IS_DURABLE VARCHAR(1) NOT NULL,

IS_NONCONCURRENT VARCHAR(1) NOT NULL,

IS_UPDATE_DATA VARCHAR(1) NOT NULL,

REQUESTS_RECOVERY VARCHAR(1) NOT NULL,

JOB_DATA BLOB NULL,

PRIMARY KEY (SCHED_NAME,JOB_NAME,JOB_GROUP)

);

CREATE TABLE QRTZ_TRIGGERS

(

SCHED_NAME VARCHAR(120) NOT NULL,

TRIGGER_NAME VARCHAR(200) NOT NULL,

TRIGGER_GROUP VARCHAR(200) NOT NULL,

JOB_NAME VARCHAR(200) NOT NULL,

JOB_GROUP VARCHAR(200) NOT NULL,

DESCRIPTION VARCHAR(250) NULL,

NEXT_FIRE_TIME BIGINT(13) NULL,

PREV_FIRE_TIME BIGINT(13) NULL,

PRIORITY INTEGER NULL,

TRIGGER_STATE VARCHAR(16) NOT NULL,

TRIGGER_TYPE VARCHAR(8) NOT NULL,

START_TIME BIGINT(13) NOT NULL,

END_TIME BIGINT(13) NULL,

CALENDAR_NAME VARCHAR(200) NULL,

MISFIRE_INSTR SMALLINT(2) NULL,

JOB_DATA BLOB NULL,

PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),

FOREIGN KEY (SCHED_NAME,JOB_NAME,JOB_GROUP)

REFERENCES QRTZ_JOB_DETAILS(SCHED_NAME,JOB_NAME,JOB_GROUP)

);

CREATE TABLE QRTZ_SIMPLE_TRIGGERS

(

SCHED_NAME VARCHAR(120) NOT NULL,

TRIGGER_NAME VARCHAR(200) NOT NULL,

TRIGGER_GROUP VARCHAR(200) NOT NULL,

REPEAT_COUNT BIGINT(7) NOT NULL,

REPEAT_INTERVAL BIGINT(12) NOT NULL,

TIMES_TRIGGERED BIGINT(10) NOT NULL,

PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),

FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)

REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)

);

CREATE TABLE QRTZ_CRON_TRIGGERS

(

SCHED_NAME VARCHAR(120) NOT NULL,

TRIGGER_NAME VARCHAR(200) NOT NULL,

TRIGGER_GROUP VARCHAR(200) NOT NULL,

CRON_EXPRESSION VARCHAR(200) NOT NULL,

TIME_ZONE_ID VARCHAR(80),

PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),

FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)

REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)

);

CREATE TABLE QRTZ_SIMPROP_TRIGGERS

(

SCHED_NAME VARCHAR(120) NOT NULL,

TRIGGER_NAME VARCHAR(200) NOT NULL,

TRIGGER_GROUP VARCHAR(200) NOT NULL,

STR_PROP_1 VARCHAR(512) NULL,

STR_PROP_2 VARCHAR(512) NULL,

STR_PROP_3 VARCHAR(512) NULL,

INT_PROP_1 INT NULL,

INT_PROP_2 INT NULL,

LONG_PROP_1 BIGINT NULL,

LONG_PROP_2 BIGINT NULL,

DEC_PROP_1 NUMERIC(13,4) NULL,

DEC_PROP_2 NUMERIC(13,4) NULL,

BOOL_PROP_1 VARCHAR(1) NULL,

BOOL_PROP_2 VARCHAR(1) NULL,

PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),

FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)

REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)

);

CREATE TABLE QRTZ_BLOB_TRIGGERS

(

SCHED_NAME VARCHAR(120) NOT NULL,

TRIGGER_NAME VARCHAR(200) NOT NULL,

TRIGGER_GROUP VARCHAR(200) NOT NULL,

BLOB_DATA BLOB NULL,

PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),

FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)

REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)

);

CREATE TABLE QRTZ_CALENDARS

(

SCHED_NAME VARCHAR(120) NOT NULL,

CALENDAR_NAME VARCHAR(200) NOT NULL,

CALENDAR BLOB NOT NULL,

PRIMARY KEY (SCHED_NAME,CALENDAR_NAME)

);

CREATE TABLE QRTZ_PAUSED_TRIGGER_GRPS

(

SCHED_NAME VARCHAR(120) NOT NULL,

TRIGGER_GROUP VARCHAR(200) NOT NULL,

PRIMARY KEY (SCHED_NAME,TRIGGER_GROUP)

);

CREATE TABLE QRTZ_FIRED_TRIGGERS

(

SCHED_NAME VARCHAR(120) NOT NULL,

ENTRY_ID VARCHAR(95) NOT NULL,

TRIGGER_NAME VARCHAR(200) NOT NULL,

TRIGGER_GROUP VARCHAR(200) NOT NULL,

INSTANCE_NAME VARCHAR(200) NOT NULL,

FIRED_TIME BIGINT(13) NOT NULL,

SCHED_TIME BIGINT(13) NOT NULL,

PRIORITY INTEGER NOT NULL,

STATE VARCHAR(16) NOT NULL,

JOB_NAME VARCHAR(200) NULL,

JOB_GROUP VARCHAR(200) NULL,

IS_NONCONCURRENT VARCHAR(1) NULL,

REQUESTS_RECOVERY VARCHAR(1) NULL,

PRIMARY KEY (SCHED_NAME,ENTRY_ID)

);

CREATE TABLE QRTZ_SCHEDULER_STATE

(

SCHED_NAME VARCHAR(120) NOT NULL,

INSTANCE_NAME VARCHAR(200) NOT NULL,

LAST_CHECKIN_TIME BIGINT(13) NOT NULL,

CHECKIN_INTERVAL BIGINT(13) NOT NULL,

PRIMARY KEY (SCHED_NAME,INSTANCE_NAME)

);

CREATE TABLE QRTZ_LOCKS

(

SCHED_NAME VARCHAR(120) NOT NULL,

LOCK_NAME VARCHAR(40) NOT NULL,

PRIMARY KEY (SCHED_NAME,LOCK_NAME)

);

5.可以写一个接口,方便扩展

6.实体类Entity

7.因为springboot都是写配置类,我们不写尽量不写配置文件,所以如下

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
html复制代码package com.example.quartz.config;

import org.quartz.Scheduler;
import org.quartz.ee.servlet.QuartzInitializerListener;
import org.springframework.beans.factory.config.PropertiesFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;

import java.io.IOException;
import java.util.Properties;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;


@Configuration
public class SchedulerConfig {

@Bean(name="SchedulerFactory")
public SchedulerFactoryBean schedulerFactoryBean() throws IOException {
SchedulerFactoryBean factory = new SchedulerFactoryBean();
factory.setQuartzProperties(quartzProperties());
return factory;
}

@Bean
public Properties quartzProperties() throws IOException {
PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
propertiesFactoryBean.setLocation(new ClassPathResource("/quartz.properties"));
//在quartz.properties中的属性被读取并注入后再初始化对象
propertiesFactoryBean.afterPropertiesSet();
return propertiesFactoryBean.getObject();
}

/*
* quartz初始化监听器
*/
@Bean
public QuartzInitializerListener executorListener() {
return new QuartzInitializerListener();
}

/*
* 通过SchedulerFactoryBean获取Scheduler的实例
*/
@Bean(name="Scheduler")
public Scheduler scheduler() throws IOException {
return schedulerFactoryBean().getScheduler();
}
}

8.主要Controller代码如下

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
html复制代码package com.example.quartz.controller;


import java.util.HashMap;
import java.util.Map;

import com.example.quartz.entity.BaseJob;
import com.example.quartz.entity.JobAndTrigger;
import com.example.quartz.service.IJobAndTriggerService;
import com.github.pagehelper.PageInfo;
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.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;



@RestController
@RequestMapping(value="/job")
public class JobController
{
@Autowired
private IJobAndTriggerService iJobAndTriggerService;

//加入Qulifier注解,通过名称注入bean
@Autowired @Qualifier("Scheduler")
private Scheduler scheduler;

private static Logger log = LoggerFactory.getLogger(JobController.class);


@PostMapping(value="/addjob")
public void addjob(@RequestParam(value="jobClassName")String jobClassName,
@RequestParam(value="jobGroupName")String jobGroupName,
@RequestParam(value="cronExpression")String cronExpression) throws Exception
{
addJob(jobClassName, jobGroupName, cronExpression);
}

public void addJob(String jobClassName, String jobGroupName, String cronExpression)throws Exception{

// 启动调度器
scheduler.start();

//构建job信息
JobDetail jobDetail = JobBuilder.newJob(getClass(jobClassName).getClass()).withIdentity(jobClassName, jobGroupName).build();

//表达式调度构建器(即任务执行的时间)
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression);

//按新的cronExpression表达式构建一个新的trigger
CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(jobClassName, jobGroupName)
.withSchedule(scheduleBuilder).build();

try {
scheduler.scheduleJob(jobDetail, trigger);

} catch (SchedulerException e) {
System.out.println("创建定时任务失败"+e);
throw new Exception("创建定时任务失败");
}
}

/**
* 暂停
* @param jobClassName
* @param jobGroupName
* @throws Exception
*/
@PostMapping(value="/pausejob")
public void pausejob(@RequestParam(value="jobClassName")String jobClassName, @RequestParam(value="jobGroupName")String jobGroupName) throws Exception
{
jobPause(jobClassName, jobGroupName);
}

public void jobPause(String jobClassName, String jobGroupName) throws Exception
{
scheduler.pauseJob(JobKey.jobKey(jobClassName, jobGroupName));
}

/**
* 恢复
* @param jobClassName
* @param jobGroupName
* @throws Exception
*/
@PostMapping(value="/resumejob")
public void resumejob(@RequestParam(value="jobClassName")String jobClassName, @RequestParam(value="jobGroupName")String jobGroupName) throws Exception
{
jobresume(jobClassName, jobGroupName);
}

public void jobresume(String jobClassName, String jobGroupName) throws Exception
{
scheduler.resumeJob(JobKey.jobKey(jobClassName, jobGroupName));
}

/**
* 重启
* @param jobClassName
* @param jobGroupName
* @param cronExpression
* @throws Exception
*/
@PostMapping(value="/reschedulejob")
public void rescheduleJob(@RequestParam(value="jobClassName")String jobClassName,
@RequestParam(value="jobGroupName")String jobGroupName,
@RequestParam(value="cronExpression")String cronExpression) throws Exception
{
jobreschedule(jobClassName, jobGroupName, cronExpression);
}

public void jobreschedule(String jobClassName, String jobGroupName, String cronExpression) throws Exception
{
try {
TriggerKey triggerKey = TriggerKey.triggerKey(jobClassName, jobGroupName);
// 表达式调度构建器
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression);

CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);

// 按新的cronExpression表达式重新构建trigger
trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();

// 按新的trigger重新设置job执行
scheduler.rescheduleJob(triggerKey, trigger);
} catch (SchedulerException e) {
System.out.println("更新定时任务失败"+e);
throw new Exception("更新定时任务失败");
}
}

/**
* 删除
* @param jobClassName
* @param jobGroupName
* @throws Exception
*/
@PostMapping(value="/deletejob")
public void deletejob(@RequestParam(value="jobClassName")String jobClassName, @RequestParam(value="jobGroupName")String jobGroupName) throws Exception
{
jobdelete(jobClassName, jobGroupName);
}

public void jobdelete(String jobClassName, String jobGroupName) throws Exception
{
scheduler.pauseTrigger(TriggerKey.triggerKey(jobClassName, jobGroupName));
scheduler.unscheduleJob(TriggerKey.triggerKey(jobClassName, jobGroupName));
scheduler.deleteJob(JobKey.jobKey(jobClassName, jobGroupName));
}


@GetMapping(value="/queryjob")
public Map<String, Object> queryjob(@RequestParam(value="pageNum")Integer pageNum, @RequestParam(value="pageSize")Integer pageSize)
{
PageInfo jobAndTrigger = iJobAndTriggerService.getJobAndTriggerDetails(pageNum, pageSize);
Map<String, Object> map = new HashMap<String, Object>();
map.put("JobAndTrigger", jobAndTrigger);
map.put("number", jobAndTrigger.getTotal());
return map;
}

public static BaseJob getClass(String classname) throws Exception
{
Class<?> class1 = Class.forName(classname);
return (BaseJob)class1.newInstance();
}


}

9.service实现类

10.最重要的Mapper文件

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
html复制代码<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.example.quartz.dao.JobAndTriggerMapper">

<resultMap id="BaseResultMap" type="com.example.quartz.entity.JobAndTrigger">
<result column="JOB_NAME" property="JOB_NAME"/>
<result column="JOB_GROUP" property="JOB_GROUP"/>
<result column="JOB_CLASS_NAME" property="JOB_CLASS_NAME"/>
<result column="TRIGGER_NAME" property="TRIGGER_NAME"/>
<result column="TRIGGER_GROUP" property="TRIGGER_GROUP"/>
<result column="REPEAT_INTERVAL" property="REPEAT_INTERVAL"/>
<result column="TIMES_TRIGGERED" property="TIMES_TRIGGERED"/>
<result column="CRON_EXPRESSION" property="CRON_EXPRESSION"/>
<result column="TIME_ZONE_ID" property="TIME_ZONE_ID"/>


</resultMap>

<select id="getJobAndTriggerDetails" resultMap="BaseResultMap">
SELECT
qrtz_job_details.JOB_NAME,
qrtz_job_details.JOB_GROUP,
qrtz_job_details.JOB_CLASS_NAME,
qrtz_triggers.TRIGGER_NAME,
qrtz_triggers.TRIGGER_GROUP,
qrtz_cron_triggers.CRON_EXPRESSION,
qrtz_cron_triggers.TIME_ZONE_ID
FROM
qrtz_job_details
JOIN qrtz_triggers
JOIN qrtz_cron_triggers ON qrtz_job_details.JOB_NAME = qrtz_triggers.JOB_NAME
AND qrtz_triggers.TRIGGER_NAME = qrtz_cron_triggers.TRIGGER_NAME
AND qrtz_triggers.TRIGGER_GROUP = qrtz_cron_triggers.TRIGGER_GROUP
AND qrtz_job_details.JOb_GROUP = qrtz_cron_triggers.TRIGGER_GROUP
</select>

</mapper>

效果

问题整理:官方提供的表中,有个暂停的记录表,但是我使用暂停过程中,确实能够暂停,但是表中无记录信息,如果正式开发中,肯定需要页面开始与暂停切换,状态的一些信息,最好是将定时器的一部分信息在存放自己生成的表中,方便业务扩展

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

0%