0%

spring-boot-starter-quartz动态调度任务实现

1.项目结构

在这里插入图片描述

2.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
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
<?xml version="1.0" encoding="UTF-8"?>  
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>com.littlefxc</groupId>
<artifactId>learn-quartz-SpringBoot</artifactId>
<version>1.0-snapshot</version>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.4.RELEASE</version>
</parent>

<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

3.数据库-模型

在jar包quartz-2.3.0.jar下有数据库sql文件.

sql文件的包路径地址:org.quartz.impl.jdbcjobstore,选择tables_mysql_innodb.sql

3.1. scheduler_job_info.sql

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
DROP TABLE IF EXISTS `scheduler_job_info`;  
CREATE TABLE `scheduler_job_info` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`cron_expression` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`cron_job` bit(1) NULL DEFAULT NULL,
`job_class` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`job_group` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`job_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`scheduler_name` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`repeat_time` bigint(20) NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `uk_job_name`(`job_name`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

SET FOREIGN_KEY_CHECKS = 1;

3.2.实体类

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
package com.littlefxc.example.quartz.enitiy;  

import lombok.Data;

import javax.persistence.*;
import java.io.Serializable;

/**
* @author fengxuechao
* @date 12/19/2018
*/
@Data
@Entity
@Table(name = "scheduler_job_info")
public class SchedulerJob implements Serializable {

private static final long serialVersionUID = -8990533448070839127L;

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Column(unique = true)
private String jobName;

private String jobGroup;

private String jobClass;

private String cronExpression;

private Long repeatTime;

private Boolean cronJob;

private String schedulerName;
}

4.配置

4.1.application.properties

com.littlefxc.example.quartz.component.CustomQuartzInstanceIdGenerator表示使用自定义的实例名生成策略,该类代码可以在5.1章节中看到,在数据库上的代码实际效果可以查看到(表qrtz_scheduler_state, 字段INSTANCE_NAME)。

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
spring.application.name=learn-quartz-SpringBoot  

# jackson Config
spring.jackson.time-zone=GMT+8
spring.jackson.date-format=yyyy-MM-dd HH:mm:sss
#spring.jackson.property-naming-strategy=SNAKE_CASE

# DataSource Config
spring.datasource.url=jdbc:mysql://localhost:3306/learn-quartz?useSSL=false
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.druid.filters=slf4j,wall
spring.datasource.druid.initial-size=1
spring.datasource.druid.min-idle=1
spring.datasource.druid.max-active=8
spring.datasource.druid.max-wait=60000
spring.datasource.druid.time-between-eviction-runs-millis=60000
spring.datasource.druid.min-evictable-idle-time-millis=300000
spring.datasource.druid.test-while-idle=true
spring.datasource.druid.test-on-borrow=false
spring.datasource.druid.test-on-return=false
spring.datasource.druid.pool-prepared-statements=true
spring.datasource.druid.max-pool-prepared-statement-per-connection-size=20

# JPA Config
spring.jpa.hibernate.ddl-auto=update
#spring.jpa.open-in-view=false
spring.jpa.show-sql=true

# Quartz Config
spring.quartz.job-store-type=jdbc
spring.quartz.jdbc.initialize-schema=never

spring.quartz.properties.org.quartz.scheduler.instanceName=${spring.application.name}
spring.quartz.properties.org.quartz.scheduler.instanceId=AUTO
spring.quartz.properties.org.quartz.scheduler.instanceIdGenerator.class=com.littlefxc.example.quartz.component.CustomQuartzInstanceIdGenerator
spring.quartz.properties.org.quartz.threadPool.threadCount=20
spring.quartz.properties.org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX
spring.quartz.properties.org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
spring.quartz.properties.org.quartz.jobStore.useProperties=true
spring.quartz.properties.org.quartz.jobStore.misfireThreshold=60000
spring.quartz.properties.org.quartz.jobStore.tablePrefix=qrtz_
spring.quartz.properties.org.quartz.jobStore.isClustered=true
spring.quartz.properties.org.quartz.plugin.shutdownHook.class=org.quartz.plugins.management.ShutdownHookPlugin
spring.quartz.properties.org.quartz.plugin.shutdownHook.cleanShutdown=TRUE

4.2.自定义SchedulerFactoryBean

创建SchedulerFactoryBean。
黄色代码高亮处表示在SchedulerFactoryBean中注入Spring上下文(applicationContext),该类(SchedulerJobFactory)可以在5.2章节中详细查看

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
package com.littlefxc.example.quartz.config;  

import com.littlefxc.example.quartz.component.SchedulerJobFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.quartz.QuartzProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;

import javax.sql.DataSource;
import java.util.Properties;

/**
* @author fengxuechao
* @date 12/19/2018
*/
@Configuration
public class SchedulerConfig {

@Autowired
private DataSource dataSource;

@Autowired
private ApplicationContext applicationContext;

@Autowired
private QuartzProperties quartzProperties;

/**
* create scheduler factory
*/
@Bean
public SchedulerFactoryBean schedulerFactoryBean() {

SchedulerJobFactory jobFactory = new SchedulerJobFactory();
jobFactory.setApplicationContext(applicationContext);

Properties properties = new Properties();
properties.putAll(quartzProperties.getProperties());

SchedulerFactoryBean factory = new SchedulerFactoryBean();
factory.setOverwriteExistingJobs(true);
factory.setDataSource(dataSource);
factory.setQuartzProperties(properties);
factory.setJobFactory(jobFactory);
return factory;
}

}

5.组件

5.1.CustomQuartzInstanceIdGenerator

用法详见4.1章节

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.littlefxc.example.quartz.component;  

import org.quartz.SchedulerException;
import org.quartz.spi.InstanceIdGenerator;

import java.util.UUID;

/**
* @author fengxuechao
* @date 12/19/2018
*/
public class CustomQuartzInstanceIdGenerator implements InstanceIdGenerator {

@Override
public String generateInstanceId() throws SchedulerException {
try {
return UUID.randomUUID().toString();
} catch (Exception ex) {
throw new SchedulerException("Couldn't generate UUID!", ex);
}
}

}

5.2.SchedulerJobFactory

Quartz与Spring结合。
在SchedulerFactory中引入Spring上下文。
用法详见4.2章节。

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
package com.littlefxc.example.quartz.component;  

import org.quartz.spi.TriggerFiredBundle;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.scheduling.quartz.SpringBeanJobFactory;

/**
* 模仿了:{@link org.springframework.boot.autoconfigure.quartz.AutowireCapableBeanJobFactory}
*
* @author fengxuechao
* @date 12/19/2018
* @see <a href="http://blog.btmatthews.com/?p=40#comment-33797">注入Spring上下文(applicationContext)
*/
public class SchedulerJobFactory extends SpringBeanJobFactory implements ApplicationContextAware {

private AutowireCapableBeanFactory beanFactory;

@Override
public void setApplicationContext(final ApplicationContext context) {
beanFactory = context.getAutowireCapableBeanFactory();
}

@Override
protected Object createJobInstance(final TriggerFiredBundle bundle) throws Exception {
final Object job = super.createJobInstance(bundle);
beanFactory.autowireBean(job);
return job;
}
}

5.3.JobScheduleCreator

Scheduler 创建Job,SimpleTrigger,CronTrigger的封装类。
用法在service 层体现。

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
package com.littlefxc.example.quartz.component;  

import lombok.extern.slf4j.Slf4j;
import org.quartz.CronTrigger;
import org.quartz.JobDataMap;
import org.quartz.JobDetail;
import org.quartz.SimpleTrigger;
import org.springframework.context.ApplicationContext;
import org.springframework.scheduling.quartz.CronTriggerFactoryBean;
import org.springframework.scheduling.quartz.JobDetailFactoryBean;
import org.springframework.scheduling.quartz.QuartzJobBean;
import org.springframework.scheduling.quartz.SimpleTriggerFactoryBean;
import org.springframework.stereotype.Component;

import java.text.ParseException;
import java.util.Date;

/**
* Scheduler创建Job, SimpleTrigger, CronTrigger
*
* @author fengxuechao
* @date 12/19/2018
* @see <a href="https://blog.csdn.net/yangshangwei/article/details/78539433#withmisfirehandlinginstructiondonothing">Quartz-错过触发机制</a>
*/
@Slf4j
@Component
public class JobScheduleCreator {

/**
* Create Quartz Job.
*
* @param jobClass Class whose executeInternal() method needs to be called.
* @param isDurable Job needs to be persisted even after completion. if true, job will be persisted, not otherwise.
* @param context Spring application context.
* @param jobName Job name.
* @param jobGroup Job group.
* @return JobDetail object
*/
public JobDetail createJob(Class<? extends QuartzJobBean> jobClass, boolean isDurable,
ApplicationContext context, String jobName, String jobGroup) {
JobDetailFactoryBean factoryBean = new JobDetailFactoryBean();
factoryBean.setJobClass(jobClass);
factoryBean.setDurability(isDurable);
factoryBean.setApplicationContext(context);
factoryBean.setName(jobName);
factoryBean.setGroup(jobGroup);

// set job data map
JobDataMap jobDataMap = new JobDataMap();
jobDataMap.put(jobName + jobGroup, jobClass.getName());
factoryBean.setJobDataMap(jobDataMap);

factoryBean.afterPropertiesSet();

return factoryBean.getObject();
}

/**
* Create cron trigger.
*
* @param triggerName Trigger name.
* @param startTime Trigger start time.
* @param cronExpression Cron expression.
* @param misFireInstruction Misfire instruction (what to do in case of misfire happens).
* @return {@link CronTrigger}
*/
public CronTrigger createCronTrigger(String triggerName, Date startTime, String cronExpression, int misFireInstruction) {
CronTriggerFactoryBean factoryBean = new CronTriggerFactoryBean();
factoryBean.setName(triggerName);
factoryBean.setStartTime(startTime);
factoryBean.setCronExpression(cronExpression);
factoryBean.setMisfireInstruction(misFireInstruction);
try {
factoryBean.afterPropertiesSet();
} catch (ParseException e) {
log.error(e.getMessage(), e);
}
return factoryBean.getObject();
}

/**
* Create simple trigger.
*
* @param triggerName Trigger name.
* @param startTime Trigger start time.
* @param repeatTime Job repeat period mills
* @param misFireInstruction Misfire instruction (what to do in case of misfire happens).
* @return {@link SimpleTrigger}
*/
public SimpleTrigger createSimpleTrigger(String triggerName, Date startTime, Long repeatTime, int misFireInstruction) {
SimpleTriggerFactoryBean factoryBean = new SimpleTriggerFactoryBean();
factoryBean.setName(triggerName);
factoryBean.setStartTime(startTime);
factoryBean.setRepeatInterval(repeatTime);
factoryBean.setRepeatCount(SimpleTrigger.REPEAT_INDEFINITELY);
factoryBean.setMisfireInstruction(misFireInstruction);
factoryBean.afterPropertiesSet();
return factoryBean.getObject();
}
}

6.Jobs

这里的任务都是实现了org.quartz.Job这个接口

Simple Job

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.littlefxc.example.quartz.jobs;  

import lombok.extern.slf4j.Slf4j;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.scheduling.quartz.QuartzJobBean;

import java.util.stream.IntStream;

/**
* @author fengxuechao
* @date 12/19/2018
*/
@Slf4j
public class SimpleJob extends QuartzJobBean {
@Override
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
log.info("{} Start................", context.getJobDetail().getKey());
IntStream.range(0, 5).forEach(i -> {
log.info("Counting - {}", i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
log.error(e.getMessage(), e);
}
});
log.info("{} End................", context.getJobDetail().getKey());
}
}

Cron Job

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
package com.littlefxc.example.quartz.jobs;  

import lombok.extern.slf4j.Slf4j;
import org.quartz.DisallowConcurrentExecution;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.scheduling.quartz.QuartzJobBean;

import java.util.stream.IntStream;

/**
* @author fengxuechao
* @date 12/19/2018
*/
@Slf4j
@DisallowConcurrentExecution // 这个注解告诉Quartz,一个给定的Job定义(也就是一个JobDetail实例),不并发运行。
public class SampleCronJob extends QuartzJobBean {
@Override
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
log.info("{} Start................", context.getJobDetail().getKey());
IntStream.range(0, 10).forEach(i -> {
log.info("Counting - {}", i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
log.error(e.getMessage(), e);
}
});
log.info("{} End................", context.getJobDetail().getKey());
}
}

7.控制器层

7.1.QuartzController

工作调度的主要代码

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
package com.littlefxc.example.quartz.controller;  

import com.littlefxc.example.quartz.enitiy.SchedulerJob;
import com.littlefxc.example.quartz.service.SchedulerService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.web.PageableDefault;
import org.springframework.web.bind.annotation.*;

import java.util.Map;

/**
* @author fengxuechao
* @date 12/19/2018
**/
@RestController
@RequestMapping("/job")
@Slf4j
public class QuartzController {

private final SchedulerService schedulerService;

@Autowired
public QuartzController(SchedulerService schedulerService) {
this.schedulerService = schedulerService;
}

/**
* 添加
*
* @param jobInfo
*/
@PostMapping(value = "/addjob")
public void addjob(@RequestBody SchedulerJob jobInfo) {
schedulerService.scheduleNewJob(jobInfo);
}

/**
* 暂停
*
* @param jobName
* @param jobGroup
*/
@PostMapping(value = "/pausejob")
public void pausejob(
@RequestParam String jobName, @RequestParam String jobGroup) {
schedulerService.pauseJob(jobName, jobGroup);
}

/**
* 恢复启动
*
* @param jobName
* @param jobGroup
*/
@PostMapping(value = "/resumejob")
public void resumejob(@RequestParam String jobName, @RequestParam String jobGroup) {
schedulerService.resumeJob(jobName, jobGroup);
}

/**
* 更新:移除older trigger,添加new trigger
*
* @param jobInfo
*/
@PostMapping(value = "/reschedulejob")
public void rescheduleJob(@RequestBody SchedulerJob jobInfo) {
schedulerService.updateScheduleJob(jobInfo);
}

/**
* 删除
*
* @param jobName
* @param jobGroup
*/
@PostMapping(value = "/deletejob")
public void deletejob(@RequestParam String jobName, @RequestParam String jobGroup) {
schedulerService.deleteJob(jobName, jobGroup);
}

/**
* 查询
*
* @param pageable
* @param cron
* @return
*/
@GetMapping(value = "/queryjob")
public Page<Map<String, Object>> queryjob(
@PageableDefault Pageable pageable, @RequestParam Boolean cron) {
return schedulerService.findAll(pageable, cron);
}
}

7.2.SchedulerController

仅对自定义数据库(scheduler_job_info)操作的控制器。

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
package com.littlefxc.example.quartz.controller;  

import com.littlefxc.example.quartz.enitiy.SchedulerJob;
import com.littlefxc.example.quartz.service.SchedulerService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

/**
* @author fengxuechao
* @date 12/20/2018
**/
@RestController
@RequestMapping("/job-info")
public class SchedulerController {

@Autowired
private SchedulerService schedulerService;

/**
* 根据jobName查询
* @param jobName
* @return {@link SchedulerJob}
*/
@GetMapping("/findOne")
public SchedulerJob findOne(@RequestParam String jobName) {
return schedulerService.findOne(jobName);
}
}

8.Service层

8.1.SchedulerServiceImpl

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
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
package com.littlefxc.example.quartz.service.impl;  

import com.littlefxc.example.quartz.component.JobScheduleCreator;
import com.littlefxc.example.quartz.enitiy.SchedulerJob;
import com.littlefxc.example.quartz.repository.SchedulerRepository;
import com.littlefxc.example.quartz.service.SchedulerService;
import lombok.extern.slf4j.Slf4j;
import org.quartz.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.scheduling.quartz.QuartzJobBean;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.List;
import java.util.Map;

/**
* @author fengxuechao
* @date 12/19/2018
*/
@Slf4j
@Transactional(rollbackFor = Exception.class)
@Service
public class SchedulerServiceImpl implements SchedulerService {

@Autowired
private SchedulerFactoryBean schedulerFactoryBean;

@Autowired
private SchedulerRepository schedulerRepository;

@Autowired
private ApplicationContext context;

@Autowired
private JobScheduleCreator scheduleCreator;

/**
* 启动所有的在表scheduler_job_info中记录的job
*/
@Override
public void startAllSchedulers() {
List<SchedulerJob> jobInfoList = schedulerRepository.findAll();
if (jobInfoList != null) {
Scheduler scheduler = schedulerFactoryBean.getScheduler();
jobInfoList.forEach(jobInfo -> {
try {
JobDetail jobDetail = JobBuilder.newJob((Class<? extends QuartzJobBean>) Class.forName(jobInfo.getJobClass()))
.withIdentity(jobInfo.getJobName(), jobInfo.getJobGroup()).build();
if (!scheduler.checkExists(jobDetail.getKey())) {
Trigger trigger;
jobDetail = scheduleCreator.createJob((Class<? extends QuartzJobBean>) Class.forName(jobInfo.getJobClass()),
false, context, jobInfo.getJobName(), jobInfo.getJobGroup());

if (jobInfo.getCronJob() && CronExpression.isValidExpression(jobInfo.getCronExpression())) {
trigger = scheduleCreator.createCronTrigger(jobInfo.getJobName(), new Date(),
jobInfo.getCronExpression(), CronTrigger.MISFIRE_INSTRUCTION_DO_NOTHING);
} else {
trigger = scheduleCreator.createSimpleTrigger(jobInfo.getJobName(), new Date(),
jobInfo.getRepeatTime(), SimpleTrigger.MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT);
}

scheduler.scheduleJob(jobDetail, trigger);

}
} catch (ClassNotFoundException e) {
log.error("Class Not Found - {}", jobInfo.getJobClass(), e);
} catch (SchedulerException e) {
log.error(e.getMessage(), e);
}
});
}
}

@Override
public void scheduleNewJob(SchedulerJob jobInfo) {
try {
Scheduler scheduler = schedulerFactoryBean.getScheduler();

JobDetail jobDetail = JobBuilder.newJob((Class<? extends QuartzJobBean>) Class.forName(jobInfo.getJobClass()))
.withIdentity(jobInfo.getJobName(), jobInfo.getJobGroup()).build();
if (!scheduler.checkExists(jobDetail.getKey())) {

jobDetail = scheduleCreator.createJob((Class<? extends QuartzJobBean>) Class.forName(jobInfo.getJobClass()),
false, context, jobInfo.getJobName(), jobInfo.getJobGroup());

Trigger trigger;
if (jobInfo.getCronJob()) {
trigger = scheduleCreator.createCronTrigger(jobInfo.getJobName(), new Date(), jobInfo.getCronExpression(),
CronTrigger.MISFIRE_INSTRUCTION_DO_NOTHING);
} else {
trigger = scheduleCreator.createSimpleTrigger(jobInfo.getJobName(), new Date(), jobInfo.getRepeatTime(),
SimpleTrigger.MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT);
}

scheduler.scheduleJob(jobDetail, trigger);
jobInfo.setSchedulerName(schedulerFactoryBean.getScheduler().getSchedulerName());
schedulerRepository.save(jobInfo);
} else {
log.error("scheduleNewJobRequest.jobAlreadyExist");
}
} catch (ClassNotFoundException e) {
log.error("Class Not Found - {}", jobInfo.getJobClass(), e);
} catch (SchedulerException e) {
log.error(e.getMessage(), e);
}
}

@Override
public void updateScheduleJob(SchedulerJob jobInfo) {
Trigger newTrigger;
if (jobInfo.getCronJob()) {
newTrigger = scheduleCreator.createCronTrigger(jobInfo.getJobName(), new Date(), jobInfo.getCronExpression(),
CronTrigger.MISFIRE_INSTRUCTION_DO_NOTHING);
} else {
newTrigger = scheduleCreator.createSimpleTrigger(jobInfo.getJobName(), new Date(), jobInfo.getRepeatTime(),
SimpleTrigger.MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT);
}
try {
schedulerFactoryBean.getScheduler().rescheduleJob(TriggerKey.triggerKey(jobInfo.getJobName()), newTrigger);
jobInfo.setSchedulerName(schedulerFactoryBean.getScheduler().getSchedulerName());
schedulerRepository.save(jobInfo);
} catch (SchedulerException e) {
log.error(e.getMessage(), e);
}
}

/**
* unscheduleJob(TriggerKey triggerKey)只是不再调度触发器,所以,当其他的触发器引用了这个Job,它们不会被改变
*
* @param jobName
* @return
*/
@Override
public boolean unScheduleJob(String jobName) {
try {
return schedulerFactoryBean.getScheduler().unscheduleJob(new TriggerKey(jobName));
} catch (SchedulerException e) {
log.error("Failed to un-schedule job - {}", jobName, e);
return false;
}
}

/**
* deleteJob(JobKey jobKey):<br>
* 1.循环遍历所有引用此Job的触发器,以取消它们的调度(to unschedule them)<br>
* 2.从jobstore中删除Job
*
* @param jobName job name
* @param jobGroup job group
* @return
*/
@Override
public boolean deleteJob(String jobName, String jobGroup) {
try {
boolean deleteJob = schedulerFactoryBean.getScheduler().deleteJob(new JobKey(jobName, jobGroup));
if (deleteJob) {
SchedulerJob job = schedulerRepository.findSchedulerJobByJobName(jobName);
schedulerRepository.delete(job);
}
return deleteJob;
} catch (SchedulerException e) {
log.error("Failed to delete job - {}", jobName, e);
return false;
}
}

/**
* 暂停
*
* @param jobName job name
* @param jobGroup job group
* @return
*/
@Override
public boolean pauseJob(String jobName, String jobGroup) {
try {
schedulerFactoryBean.getScheduler().pauseJob(new JobKey(jobName, jobGroup));
return true;
} catch (SchedulerException e) {
log.error("Failed to pause job - {}", jobName, e);
return false;
}
}

/**
* 恢复
*
* @param jobName job name
* @param jobGroup job group
* @return
*/
@Override
public boolean resumeJob(String jobName, String jobGroup) {
try {
schedulerFactoryBean.getScheduler().resumeJob(new JobKey(jobName, jobGroup));
return true;
} catch (SchedulerException e) {
log.error("Failed to resume job - {}", jobName, e);
return false;
}
}

@Override
public boolean startJobNow(String jobName, String jobGroup) {
try {
schedulerFactoryBean.getScheduler().triggerJob(new JobKey(jobName, jobGroup));
return true;
} catch (SchedulerException e) {
log.error("Failed to start new job - {}", jobName, e);
return false;
}
}

/**
* 分页查询
*
* @param pageable
* @param cron true: cron trigger, false: simple trigger
* @return
*/
@Transactional(readOnly = true)// 方法上注解属性会覆盖类注解上的相同属性
@Override
public Page<Map<String, Object>> findAll(Pageable pageable, Boolean cron) {
if (cron) {
return schedulerRepository.getJobWithCronTrigger(pageable);
} else {
return schedulerRepository.getJobWithSimpleTrigger(pageable);
}
}

/**
* 根据jobName查询单条记录
*
* @param jobName
* @return
*/
@Transactional(readOnly = true)
@Override
public SchedulerJob findOne(String jobName) {
return schedulerRepository.findSchedulerJobByJobName(jobName);
}

}

9.Dao层

9.1.SchedulerRepository

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
package com.littlefxc.example.quartz.repository;  

import com.littlefxc.example.quartz.enitiy.SchedulerJob;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;

import java.util.Map;

/**
* @author fengxuechao
* @date 12/19/2018
*/
@Repository
public interface SchedulerRepository extends JpaRepository<SchedulerJob, Long> {

/**
* 仅查询simple trigger关联的Job
* 不查询cron trigger关联的job
*
* @param pageable 分页信息
* @return
*/
@Query(value = "select " +
"j.JOB_NAME, " +
"j.JOB_GROUP, " +
"j.JOB_CLASS_NAME, " +
"t.TRIGGER_NAME, " +
"t.TRIGGER_GROUP, " +
"s.REPEAT_INTERVAL, " +
"s.TIMES_TRIGGERED " +
"from qrtz_job_details as j " +
"join qrtz_triggers as t " +
"join qrtz_simple_triggers as s ON j.JOB_NAME = t.JOB_NAME " +
"and t.TRIGGER_NAME = s.TRIGGER_NAME " +
"and t.TRIGGER_GROUP = s.TRIGGER_GROUP " +
"where j.SCHED_NAME = 'schedulerFactoryBean' " +
"order by ?#{#pageable}",
countQuery = "select count(1) " +
"from qrtz_job_details as j " +
"join qrtz_triggers as t " +
"join qrtz_cron_triggers as c ON j.JOB_NAME = t.JOB_NAME " +
"and t.TRIGGER_NAME = c.TRIGGER_NAME " +
"and t.TRIGGER_GROUP = c.TRIGGER_GROUP " +
"where j.SCHED_NAME = 'schedulerFactoryBean' ",
nativeQuery = true)
Page<Map<String, Object>> getJobWithSimpleTrigger(Pageable pageable);

/**
* 仅查询cron trigger关联的Job
* 不查询simple trigger关联的job
*
* @param pageable
* @return
*/
@Query(value = "select " +
"j.JOB_NAME, " +
"j.JOB_GROUP, " +
"j.JOB_CLASS_NAME, " +
"t.TRIGGER_NAME, " +
"t.TRIGGER_GROUP, " +
"c.CRON_EXPRESSION, " +
"c.TIME_ZONE_ID " +
"from qrtz_job_details as j " +
"join qrtz_triggers as t " +
"join qrtz_cron_triggers as c ON j.JOB_NAME = t.JOB_NAME " +
"and t.TRIGGER_NAME = c.TRIGGER_NAME " +
"and t.TRIGGER_GROUP = c.TRIGGER_GROUP " +
"where j.SCHED_NAME = 'schedulerFactoryBean' " +
"order by ?#{#pageable}",
countQuery = "select count(1) " +
"from qrtz_job_details as j " +
"join qrtz_triggers as t " +
"join qrtz_cron_triggers as c ON j.JOB_NAME = t.JOB_NAME " +
"and t.TRIGGER_NAME = c.TRIGGER_NAME " +
"and t.TRIGGER_GROUP = c.TRIGGER_GROUP " +
"where j.SCHED_NAME = 'schedulerFactoryBean' ",
nativeQuery = true)
Page<Map<String, Object>> getJobWithCronTrigger(Pageable pageable);

/**
* 根据JobName查询SchedulerJob
*
* @param jobName
* @return SchedulerJob
*/
SchedulerJob findSchedulerJobByJobName(String jobName);
}

10.网页Vue+ElementUI实现

10.1.simple.html

仅对Simple Trigger管理

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
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
<!DOCTYPE html>  
<html>
<head>
<meta charset="UTF-8">
<title>QuartzDemo</title>
<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://cdn.bootcss.com/vue-resource/1.5.1/vue-resource.js"></script>
<script src="https://unpkg.com/element-ui/lib/index.js"></script>

<style>
#top {
background: #20A0FF;
padding: 5px;
overflow: hidden
}
</style>

</head>
<body>
<div id="test">

<a href="cron.html">goto simple.html</a>

<div id="top">
<el-button type="text" @click="search" style="color:white">查询</el-button>
<el-button type="text" @click="handleadd" style="color:white">添加</el-button>
</span>
</div>

<br/>

<div style="margin-top:15px">

<el-table
ref="testTable"
:data="tableData"
style="width:100%"
border
>
<el-table-column
prop="JOB_NAME"
label="任务名称"
sortable
show-overflow-tooltip>
</el-table-column>

<el-table-column
prop="JOB_GROUP"
label="任务所在组"
sortable>
</el-table-column>

<el-table-column
prop="JOB_CLASS_NAME"
label="任务类名"
sortable>
</el-table-column>

<el-table-column
prop="TRIGGER_NAME"
label="触发器名称"
sortable>
</el-table-column>

<el-table-column
prop="TRIGGER_GROUP"
label="触发器所在组"
sortable>
</el-table-column>

<el-table-column
prop="REPEAT_INTERVAL"
label="触发间隔(毫秒)"
sortable>
</el-table-column>

<el-table-column
prop="TIMES_TRIGGERED"
label="已触发次数"
sortable>
</el-table-column>

<el-table-column label="操作" width="300">
<template scope="scope">
<el-button
size="small"
type="warning"
@click="handlePause(scope.$index, scope.row)">暂停
</el-button>

<el-button
size="small"
type="info"
@click="handleResume(scope.$index, scope.row)">恢复
</el-button>

<el-button
size="small"
type="danger"
@click="handleDelete(scope.$index, scope.row)">删除
</el-button>

<el-button
size="small"
type="success"
@click="handleUpdate(scope.$index, scope.row)">修改
</el-button>
</template>
</el-table-column>
</el-table>

<div align="center">
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="currentPage"
:page-sizes="[10, 20, 30, 40]"
:page-size="pagesize"
layout="total, sizes, prev, pager, next, jumper"
:total="totalCount">
</el-pagination>
</div>
</div>

<el-dialog title="添加任务" :visible.syn="dialogFormVisible">
<el-form :model="form">
<el-form-item label="任务名称" label-width="120px" style="width:35%">
<el-input v-model="form.jobName" auto-complete="off"></el-input>
</el-form-item>
<el-form-item label="任务分组" label-width="120px" style="width:35%">
<el-input v-model="form.jobGroup" auto-complete="off"></el-input>
</el-form-item>
<el-form-item label="任务类名" label-width="120px" style="width:35%">
<el-input v-model="form.jobClass" auto-complete="off"></el-input>
</el-form-item>
<el-form-item label="触发器类型" label-width="120px" style="width:35%">
<el-switch
v-model="form.cronJob"
active-color="#13ce66"
inactive-color="#ff4949"
active-text="cron"
inactive-text="simple">
</el-switch>
</el-form-item>
<el-form-item label="表达式" v-show="form.cronJob" label-width="120px" style="width:35%">
<el-input v-model="form.cronExpression" auto-complete="off"></el-input>
</el-form-item>
<el-form-item label="触发间隔(毫秒)" v-show="!form.cronJob" label-width="120px" style="width:35%">
<el-input v-model="form.repeatTime" auto-complete="off"></el-input>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="dialogFormVisible = false">取 消</el-button>
<el-button type="primary" @click="add">确 定</el-button>
</div>
</el-dialog>

<el-dialog title="修改任务" :visible.syn="updateFormVisible">
<el-form :model="updateform">
<el-form-item label="任务名称" label-width="120px" style="width:35%">
<el-input v-model="updateform.jobName" auto-complete="off"></el-input>
</el-form-item>
<el-form-item label="任务分组" label-width="120px" style="width:35%">
<el-input v-model="updateform.jobGroup" auto-complete="off"></el-input>
</el-form-item>
<el-form-item label="任务类名" label-width="120px" style="width:35%">
<el-input v-model="updateform.jobClass" auto-complete="off"></el-input>
</el-form-item>
<el-form-item label="触发器类型" label-width="120px" style="width:35%">
<el-switch
v-model="updateform.cronJob"
active-color="#13ce66"
inactive-color="#ff4949"
active-text="cron"
inactive-text="simple">
</el-switch>
</el-form-item>
<el-form-item label="表达式" v-show="updateform.cronJob" label-width="120px" style="width:35%">
<el-input v-model="updateform.cronExpression" auto-complete="off"></el-input>
</el-form-item>
<el-form-item label="触发间隔(毫秒)" v-show="!updateform.cronJob" label-width="120px" style="width:35%">
<el-input v-model="updateform.repeatTime" auto-complete="off"></el-input>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="updateFormVisible = false">取 消</el-button>
<el-button type="primary" @click="update">确 定</el-button>
</div>
</el-dialog>

</div>

<footer align="center">
<p>© Quartz 任务管理</p>
</footer>

<script>
var vue = new Vue({
el: "#test",
data: {
//表格当前页数据
tableData: [],

//请求的URL
url: '',

//默认每页数据量
pagesize: 10,

//当前页码
currentPage: 1,

//查询的页码
start: 1,

//默认数据总数
totalCount: 1000,

//添加对话框默认可见性
dialogFormVisible: false,

//修改对话框默认可见性
updateFormVisible: false,

//提交的表单
form: {
jobName: '',
jobGroup: '',
jobClass: '',
cronJob: false,
repeatTime: 0,
cronExpression: ''
},

// 修改的表单
updateform: {
id: 0,
jobName: '',
jobGroup: '',
jobClass: '',
cronJob: false,
repeatTime: 0,
cronExpression: ''
},
},

methods: {

//从服务器读取数据
loadData: function (pageNum, pageSize) {
this.$http.get('job/queryjob?cron=false&' + 'page=' + pageNum + '&size=' + pageSize).then(function (res) {
console.log(res);
this.tableData = res.body.content;
this.totalCount = res.body.numberOfElements;
}, function () {
console.log('failed');
});
},

//单行删除
handleDelete: function (index, row) {
this.$http.post('job/deletejob', {
"jobName": row.JOB_NAME,
"jobGroup": row.JOB_GROUP
}, {emulateJSON: true}).then(function (res) {
this.loadData(this.currentPage, this.pagesize);
}, function () {
console.log('failed');
});
},

//暂停任务
handlePause: function (index, row) {
this.$http.post('job/pausejob', {
"jobName": row.JOB_NAME,
"jobGroup": row.JOB_GROUP
}, {emulateJSON: true}).then(function (res) {
this.loadData(this.currentPage, this.pagesize);
}, function () {
console.log('failed');
});
},

//恢复任务
handleResume: function (index, row) {
this.$http.post('job/resumejob', {
"jobName": row.JOB_NAME,
"jobGroup": row.JOB_GROUP
}, {emulateJSON: true}).then(function (res) {
this.loadData(this.currentPage, this.pagesize);
}, function () {
console.log('failed');
});
},

//搜索
search: function () {
this.loadData(this.currentPage, this.pagesize);
},

//弹出对话框
handleadd: function () {
this.dialogFormVisible = true;
},

//添加
add: function () {
this.$http.post('job/addjob', this.form, {
headers: {'Content-Type': "application/json;charset=utf-8"}
}).then(function (res) {
this.loadData(this.currentPage, this.pagesize);
this.dialogFormVisible = false;
}, function () {
console.log('failed');
});
},

//更新
handleUpdate: function (index, row) {
console.log(row);
this.updateFormVisible = true;
this.updateform.jobName = row.JOB_NAME;
this.updateform.jobGroup = row.JOB_GROUP;
this.updateform.jobClass = row.JOB_CLASS_NAME;
this.updateform.cronJob = false;
this.updateform.repeatTime = row.REPEAT_INTERVAL;
this.$http.get('job-info/findOne?jobName=' + row.JOB_NAME).then(function (res) {
this.updateform.id = res.body.id;
this.updateform.cronExpression = row.cronExpression;
}, function () {
console.log('failed');
});
console.log(this.updateform)
},

//更新任务
update: function () {
this.$http.post('job/reschedulejob', this.updateform, {
headers: {'Content-Type': "application/json;charset=utf-8"}
}).then(function (res) {
this.loadData(this.currentPage, this.pagesize);
this.updateFormVisible = false;
}, function () {
console.log('failed');
});

},

//每页显示数据量变更
handleSizeChange: function (val) {
this.pagesize = val;
this.loadData(this.currentPage, this.pagesize);
},

//页码变更
handleCurrentChange: function (val) {
this.currentPage = val;
this.loadData(this.currentPage, this.pagesize);
},
},

});

//载入数据
vue.loadData(vue.currentPage, vue.pagesize);
</script>

</body>
</html>

10.2.cron.html

进队Cron Trigger 管理

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
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
<!DOCTYPE html>  
<html>
<head>
<meta charset="UTF-8">
<title>QuartzDemo</title>
<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="http://cdn.bootcss.com/vue-resource/1.3.4/vue-resource.js"></script>
<script src="https://unpkg.com/element-ui/lib/index.js"></script>

<style>
#top {
background: #20A0FF;
padding: 5px;
overflow: hidden
}
</style>

</head>
<body>
<div id="test">

<a href="simple.html">goto cron.html</a>

<div id="top">
<el-button type="text" @click="search" style="color:white">查询</el-button>
<el-button type="text" @click="handleadd" style="color:white">添加</el-button>
</span>
</div>

<br/>

<div style="margin-top:15px">

<el-table
ref="testTable"
:data="tableData"
style="width:100%"
border
>
<el-table-column
prop="JOB_NAME"
label="任务名称"
sortable
show-overflow-tooltip>
</el-table-column>

<el-table-column
prop="JOB_GROUP"
label="任务所在组"
sortable>
</el-table-column>

<el-table-column
prop="JOB_CLASS_NAME"
label="任务类名"
sortable>
</el-table-column>

<el-table-column
prop="TRIGGER_NAME"
label="触发器名称"
sortable>
</el-table-column>

<el-table-column
prop="TRIGGER_GROUP"
label="触发器所在组"
sortable>
</el-table-column>

<el-table-column
prop="CRON_EXPRESSION"
label="表达式"
sortable>
</el-table-column>

<el-table-column
prop="TIME_ZONE_ID"
label="时区"
sortable>
</el-table-column>

<el-table-column label="操作" width="300">
<template scope="scope">
<el-button
size="small"
type="warning"
@click="handlePause(scope.$index, scope.row)">暂停
</el-button>

<el-button
size="small"
type="info"
@click="handleResume(scope.$index, scope.row)">恢复
</el-button>

<el-button
size="small"
type="danger"
@click="handleDelete(scope.$index, scope.row)">删除
</el-button>

<el-button
size="small"
type="success"
@click="handleUpdate(scope.$index, scope.row)">修改
</el-button>
</template>
</el-table-column>
</el-table>

<div align="center">
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="currentPage"
:page-sizes="[10, 20, 30, 40]"
:page-size="pagesize"
layout="total, sizes, prev, pager, next, jumper"
:total="totalCount">
</el-pagination>
</div>
</div>

<el-dialog title="添加任务" :visible.syn="dialogFormVisible">
<el-form :model="form">
<el-form-item label="任务名称" label-width="120px" style="width:35%">
<el-input v-model="form.jobName" auto-complete="off"></el-input>
</el-form-item>
<el-form-item label="任务分组" label-width="120px" style="width:35%">
<el-input v-model="form.jobGroup" auto-complete="off"></el-input>
</el-form-item>
<el-form-item label="任务类名" label-width="120px" style="width:35%">
<el-input v-model="form.jobClass" auto-complete="off"></el-input>
</el-form-item>
<el-form-item label="触发器类型" label-width="120px" style="width:35%">
<el-switch
v-model="form.cronJob"
active-color="#13ce66"
inactive-color="#ff4949"
active-text="cron"
inactive-text="simple">
</el-switch>
</el-form-item>
<el-form-item label="表达式" v-show="form.cronJob" label-width="120px" style="width:35%">
<el-input v-model="form.cronExpression" auto-complete="off"></el-input>
</el-form-item>
<el-form-item label="触发间隔(毫秒)" v-show="!form.cronJob" label-width="120px" style="width:35%">
<el-input v-model="form.repeatTime" auto-complete="off"></el-input>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="dialogFormVisible = false">取 消</el-button>
<el-button type="primary" @click="add">确 定</el-button>
</div>
</el-dialog>

<el-dialog title="修改任务" :visible.syn="updateFormVisible">
<el-form :model="updateform">
<el-form-item label="任务名称" label-width="120px" style="width:35%">
<el-input v-model="updateform.jobName" auto-complete="off"></el-input>
</el-form-item>
<el-form-item label="任务分组" label-width="120px" style="width:35%">
<el-input v-model="updateform.jobGroup" auto-complete="off"></el-input>
</el-form-item>
<el-form-item label="任务类名" label-width="120px" style="width:35%">
<el-input v-model="updateform.jobClass" auto-complete="off"></el-input>
</el-form-item>
<el-form-item label="触发器类型" label-width="120px" style="width:35%">
<el-switch
v-model="updateform.cronJob"
active-color="#13ce66"
inactive-color="#ff4949"
active-text="cron"
inactive-text="simple">
</el-switch>
</el-form-item>
<el-form-item label="表达式" v-show="updateform.cronJob" label-width="120px" style="width:35%">
<el-input v-model="updateform.cronExpression" auto-complete="off"></el-input>
</el-form-item>
<el-form-item label="触发间隔(毫秒)" v-show="!updateform.cronJob" label-width="120px" style="width:35%">
<el-input v-model="updateform.repeatTime" auto-complete="off"></el-input>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="updateFormVisible = false">取 消</el-button>
<el-button type="primary" @click="update">确 定</el-button>
</div>
</el-dialog>

</div>

<footer align="center">
<p>© Quartz 任务管理</p>
</footer>

<script>
var vue = new Vue({
el: "#test",
data: {
//表格当前页数据
tableData: [],

//请求的URL
url: 'job/queryjob',

//默认每页数据量
pagesize: 10,

//当前页码
currentPage: 1,

//查询的页码
start: 1,

//默认数据总数
totalCount: 1000,

//添加对话框默认可见性
dialogFormVisible: false,

//修改对话框默认可见性
updateFormVisible: false,

//提交的表单
form: {
jobName: '',
jobGroup: '',
jobClass: '',
cronJob: true,
repeatTime: 0,
cronExpression: ''
},

// 修改的表单
updateform: {
id: 0,
jobName: '',
jobGroup: '',
jobClass: '',
cronJob: true,
repeatTime: 0,
cronExpression: ''
},
},

methods: {

//从服务器读取数据
loadData: function (pageNum, pageSize) {
this.$http.get('job/queryjob?cron=true&' + 'page=' + pageNum + '&size=' + pageSize).then(function (res) {
console.log(res);
this.tableData = res.body.content;
this.totalCount = res.body.numberOfElements;
}, function () {
console.log('failed');
});
},

//单行删除
handleDelete: function (index, row) {
this.$http.post('job/deletejob', {
"jobName": row.JOB_NAME,
"jobGroup": row.JOB_GROUP
}, {emulateJSON: true}).then(function (res) {
this.loadData(this.currentPage, this.pagesize);
}, function () {
console.log('failed');
});
},

//暂停任务
handlePause: function (index, row) {
this.$http.post('job/pausejob', {
"jobName": row.JOB_NAME,
"jobGroup": row.JOB_GROUP
}, {emulateJSON: true}).then(function (res) {
this.loadData(this.currentPage, this.pagesize);
}, function () {
console.log('failed');
});
},

//恢复任务
handleResume: function (index, row) {
this.$http.post('job/resumejob', {
"jobName": row.JOB_NAME,
"jobGroup": row.JOB_GROUP
}, {emulateJSON: true}).then(function (res) {
this.loadData(this.currentPage, this.pagesize);
}, function () {
console.log('failed');
});
},

//搜索
search: function () {
this.loadData(this.currentPage, this.pagesize);
},

//弹出对话框
handleadd: function () {
this.dialogFormVisible = true;
},

//添加
add: function () {
this.$http.post('job/addjob', this.form, {
headers: {'Content-Type': "application/json;charset=utf-8"}
}).then(function (res) {
this.loadData(this.currentPage, this.pagesize);
this.dialogFormVisible = false;
}, function () {
console.log('failed');
});
},

//更新
handleUpdate: function (index, row) {
console.log(row);
this.updateFormVisible = true;
this.updateform.jobName = row.JOB_NAME;
this.updateform.jobGroup = row.JOB_GROUP;
this.updateform.jobClass = row.JOB_CLASS_NAME;
this.updateform.cronJob = true;
this.updateform.cronExpression = row.CRON_EXPRESSION;
this.$http.get('job-info/findOne?jobName=' + row.JOB_NAME).then(function (res) {
this.updateform.id = res.body.id;
this.updateform.repeatTime = res.body.repeatTime;
}, function () {
console.log('failed');
});
console.log(this.updateform)
},

//更新任务
update: function () {
this.$http.post('job/reschedulejob', this.updateform, {
headers: {'Content-Type': "application/json;charset=utf-8"}
}).then(function (res) {
this.loadData(this.currentPage, this.pagesize);
this.updateFormVisible = false;
}, function () {
console.log('failed');
});

},

//每页显示数据量变更
handleSizeChange: function (val) {
this.pagesize = val;
this.loadData(this.currentPage, this.pagesize);
},

//页码变更
handleCurrentChange: function (val) {
this.currentPage = val;
this.loadData(this.currentPage, this.pagesize);
},
},

});
//载入数据
vue.loadData(vue.currentPage, vue.pagesize);
</script>

</body>
</html>

11. 引用

https://blog.csdn.net/u012907049/article/details/73801122
https://blog.csdn.net/yangshangwei/article/details/78539433#withmisfirehandlinginstructiondonothing
http://blog.btmatthews.com/?p=40#comment-33797
https://www.baeldung.com/spring-quartz-schedule