說明:
核心框架: SpringBoot 2.1.2
任務(wù)調(diào)度框架: Quartz 2.3.0
持久層框架: mybatis-plus 3.0.7
數(shù)據(jù)庫:mysql 8.0.12
1.項(xiàng)目目錄結(jié)構(gòu)

image.png
2.數(shù)據(jù)庫表結(jié)構(gòu)
DROP TABLE IF EXISTS `schedule_job`;
CREATE TABLE `schedule_job` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '任務(wù)id',
`job_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '任務(wù)名稱',
`cron_expression` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 'cron表達(dá)式',
`bean_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '服務(wù)名稱',
`method_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '方法名稱',
`status` int(1) NOT NULL DEFAULT 1 COMMENT '狀態(tài) 1.啟動(dòng) 2.暫停',
`delete_flag` tinyint(1) NOT NULL DEFAULT 0 COMMENT '是否刪除 0.否 1.是',
`creator_id` int(11) NULL DEFAULT NULL COMMENT '創(chuàng)建人id',
`creator_name` varchar(45) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '創(chuàng)建人',
`created_time` datetime(0) NULL DEFAULT NULL COMMENT '創(chuàng)建時(shí)間',
`updated_time` datetime(0) NULL DEFAULT NULL COMMENT '更新時(shí)間',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
SET FOREIGN_KEY_CHECKS = 1;
3.pom.xml
<?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>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!--Web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--Quartz-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
<!--Mysql驅(qū)動(dòng)-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.11</version>
</dependency>
<!--Mybatis-Plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.0.7</version>
</dependency>
<!--Test-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
4.application.yml
server:
port: 8080
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/lanjerry?useUnicode=yes&characterEncoding=UTF8&useSSL=false&serverTimezone=CTT&allowMultiQueries=true
username: root
password: 123456
hikari:
max-lifetime: 1765000
maximum-pool-size: 15
aop:
proxy-target-class: true
auto: true
#mybatis-plus配置
mybatis-plus:
global-config:
db-config:
field-strategy: not_empty
id-type: auto
db-type: mysql
logic-delete-value: 1
logic-not-delete-value: 0
banner: false
mapper-locations: classpath:/mapper/*Mapper.xml
type-aliases-package: com.example.demo.domain
5.Quartz配置:QuartzConfig.class
@Configuration
public class QuartzConfig {
@Bean
public SchedulerFactoryBean schedulerFactoryBean(){
SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
schedulerFactoryBean.setOverwriteExistingJobs(true);//覆蓋已存在的任務(wù)
schedulerFactoryBean.setStartupDelay(60);//延時(shí)60秒啟動(dòng)定時(shí)任務(wù),避免系統(tǒng)未完全啟動(dòng)卻開始執(zhí)行定時(shí)任務(wù)的情況
return schedulerFactoryBean;
}
// 創(chuàng)建schedule
@Bean(name = "scheduler")
public Scheduler scheduler() {
return schedulerFactoryBean().getScheduler();
}
}
6.實(shí)體:ScheduleJob.class,枚舉:JobOperateEnum
@TableName("schedule_job")
public class ScheduleJob implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
/**
* 任務(wù)名稱
*/
private String jobName;
/**
* cron表達(dá)式
*/
private String cronExpression;
/**
* 服務(wù)名稱
*/
private String beanName;
/**
* 方法名稱
*/
private String methodName;
/**
* 狀態(tài) 1.啟動(dòng) 2.暫停
*/
private int status;
/**
* 是否刪除 0.否 1.是
*/
@TableField(fill = FieldFill.INSERT)
@TableLogic
private Boolean deleteFlag;
/**
* 創(chuàng)建人id
*/
@TableField(fill = FieldFill.INSERT)
private Integer creatorId;
/**
* 創(chuàng)建人
*/
@TableField(fill = FieldFill.INSERT)
private String creatorName;
/**
* 創(chuàng)建時(shí)間
*/
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createdTime;
/**
* 更新時(shí)間
*/
@TableField(fill = FieldFill.UPDATE)
private LocalDateTime updatedTime;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getJobName() {
return jobName;
}
public void setJobName(String jobName) {
this.jobName = jobName;
}
public String getCronExpression() {
return cronExpression;
}
public void setCronExpression(String cronExpression) {
this.cronExpression = cronExpression;
}
public String getBeanName() {
return beanName;
}
public void setBeanName(String beanName) {
this.beanName = beanName;
}
public String getMethodName() {
return methodName;
}
public void setMethodName(String methodName) {
this.methodName = methodName;
}
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
public Boolean getDeleteFlag() {
return deleteFlag;
}
public void setDeleteFlag(Boolean deleteFlag) {
this.deleteFlag = deleteFlag;
}
public Integer getCreatorId() {
return creatorId;
}
public void setCreatorId(Integer creatorId) {
this.creatorId = creatorId;
}
public String getCreatorName() {
return creatorName;
}
public void setCreatorName(String creatorName) {
this.creatorName = creatorName;
}
public LocalDateTime getCreatedTime() {
return createdTime;
}
public void setCreatedTime(LocalDateTime createdTime) {
this.createdTime = createdTime;
}
public LocalDateTime getUpdatedTime() {
return updatedTime;
}
public void setUpdatedTime(LocalDateTime updatedTime) {
this.updatedTime = updatedTime;
}
}
public enum JobOperateEnum {
START(1, "啟動(dòng)"),
PAUSE(2, "暫停"),
DELETE(3, "刪除");
private final Integer value;
private final String desc;
JobOperateEnum(final Integer value, final String desc) {
this.value = value;
this.desc = desc;
}
public Serializable getValue() {
return this.value;
}
// Jackson 注解為 JsonValue 返回中文 json 描述
public String getDesc() {
return this.desc;
}
public String getEnumName() {
return name();
}
}
7.任務(wù)的調(diào)度工廠(主要是實(shí)現(xiàn)具體的執(zhí)行)QuartzFactory.class
public class QuartzFactory implements Job {
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
//獲取調(diào)度數(shù)據(jù)
ScheduleJob scheduleJob = (ScheduleJob) jobExecutionContext.getMergedJobDataMap().get("scheduleJob");
//獲取對(duì)應(yīng)的Bean
Object object = SpringUtil.getBean(scheduleJob.getBeanName());
try {
//利用反射執(zhí)行對(duì)應(yīng)方法
Method method = object.getClass().getMethod(scheduleJob.getMethodName());
method.invoke(object);
} catch (Exception e) {
e.printStackTrace();
}
}
}
8.具體的任務(wù)調(diào)度以及觸發(fā)設(shè)置:QuartzServiceImpl.class(略過接口)
@Service
@Transactional
public class QuartzServiceImpl implements QuartzService {
/**
* 調(diào)度器
*/
@Autowired
private Scheduler scheduler;
@Autowired
private ScheduleJobService jobService;
@Override
public void timingTask() {
//查詢數(shù)據(jù)庫是否存在需要定時(shí)的任務(wù)
List<ScheduleJob> scheduleJobs = jobService.list();
if (scheduleJobs != null) {
scheduleJobs.forEach(this::addJob);
}
}
@Override
public void addJob(ScheduleJob job) {
try {
//創(chuàng)建觸發(fā)器
Trigger trigger = TriggerBuilder.newTrigger().withIdentity(job.getJobName())
.withSchedule(CronScheduleBuilder.cronSchedule(job.getCronExpression()))
.startNow()
.build();
//創(chuàng)建任務(wù)
JobDetail jobDetail = JobBuilder.newJob(QuartzFactory.class)
.withIdentity(job.getJobName())
.build();
//傳入調(diào)度的數(shù)據(jù),在QuartzFactory中需要使用
jobDetail.getJobDataMap().put("scheduleJob", job);
//調(diào)度作業(yè)
scheduler.scheduleJob(jobDetail, trigger);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
public void operateJob(JobOperateEnum jobOperateEnum, ScheduleJob job) throws SchedulerException {
JobKey jobKey = new JobKey(job.getJobName());
JobDetail jobDetail = scheduler.getJobDetail(jobKey);
if (jobDetail == null) {
//拋異常
}
switch (jobOperateEnum) {
case START:
scheduler.resumeJob(jobKey);
break;
case PAUSE:
scheduler.pauseJob(jobKey);
break;
case DELETE:
scheduler.deleteJob(jobKey);
break;
}
}
@Override
public void startAllJob() throws SchedulerException {
scheduler.start();
}
@Override
public void pauseAllJob() throws SchedulerException {
scheduler.standby();
}
}
9.實(shí)現(xiàn)CommandLineRunner接口,以達(dá)到開機(jī)啟動(dòng)執(zhí)行:JobSchedule.class
@Component
public class JobSchedule implements CommandLineRunner {
@Autowired
private QuartzService taskService;
/**
* 任務(wù)調(diào)度開始
* @param strings
* @throws Exception
*/
@Override
public void run(String... strings) throws Exception {
System.out.println("任務(wù)調(diào)度開始==============任務(wù)調(diào)度開始");
taskService.timingTask();
System.out.println("任務(wù)調(diào)度結(jié)束==============任務(wù)調(diào)度結(jié)束");
}
}
10.基礎(chǔ)的配置完成,以下是幾個(gè)簡單的例子
執(zhí)行的Job1:
@Component("testJob01")
@Transactional
public class TestJob01 {
public void execute() {
System.out.println("-------------------TestJob01任務(wù)執(zhí)行開始-------------------");
System.out.println(new Date());
System.out.println("-------------------TestJob01任務(wù)執(zhí)行結(jié)束-------------------");
}
}
執(zhí)行的Job2:
@Component("testJob02")
@Transactional
public class TestJob02 {
public void execute() {
System.out.println("-------------------TestJob02任務(wù)執(zhí)行開始-------------------");
System.out.println(new Date());
System.out.println("-------------------TestJob02任務(wù)執(zhí)行結(jié)束-------------------");
}
}
配置的數(shù)據(jù)庫:

image.png
11.業(yè)務(wù)類實(shí)現(xiàn):ScheduleJobServiceImpl.class(略過接口)
@Service
@Transactional
public class ScheduleJobServiceImpl extends ServiceImpl<ScheduleJobMapper, ScheduleJob> implements ScheduleJobService {
@Autowired
private QuartzService quartzService;
@Override
public void add(ScheduleJob job) {
//此處省去數(shù)據(jù)驗(yàn)證
this.save(job);
//加入job
try {
quartzService.addJob(job);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void start(int id) {
//此處省去數(shù)據(jù)驗(yàn)證
ScheduleJob job = this.getById(id);
job.setStatus(1);
this.updateById(job);
//執(zhí)行job
try {
quartzService.operateJob(JobOperateEnum.START, job);
} catch (SchedulerException e) {
e.printStackTrace();
}
}
@Override
public void pause(int id) {
//此處省去數(shù)據(jù)驗(yàn)證
ScheduleJob job = this.getById(id);
job.setStatus(2);
this.updateById(job);
//執(zhí)行job
try {
quartzService.operateJob(JobOperateEnum.PAUSE, job);
} catch (SchedulerException e) {
e.printStackTrace();
}
}
@Override
public void delete(int id) {
//此處省去數(shù)據(jù)驗(yàn)證
ScheduleJob job = this.getById(id);
this.removeById(id);
//執(zhí)行job
try {
quartzService.operateJob(JobOperateEnum.DELETE, job);
} catch (SchedulerException e) {
e.printStackTrace();
}
}
@Override
public void startAllJob() {
//此處省去數(shù)據(jù)驗(yàn)證
ScheduleJob job = new ScheduleJob();
job.setStatus(1);
this.update(job, new QueryWrapper<>());
//執(zhí)行job
try {
quartzService.startAllJob();
} catch (SchedulerException e) {
e.printStackTrace();
}
}
@Override
public void pauseAllJob() {
//此處省去數(shù)據(jù)驗(yàn)證
ScheduleJob job = new ScheduleJob();
job.setStatus(2);
this.update(job, new QueryWrapper<>());
//執(zhí)行job
try {
quartzService.pauseAllJob();
} catch (SchedulerException e) {
e.printStackTrace();
}
}
}
12.Controller類:ScheduleJobController.class
@RestController
@RequestMapping("/job")
public class ScheduleJobController {
@Autowired
private ScheduleJobService jobService;
@GetMapping(value = "/hello")
public String helloSpringBoot() {
return "Hello quartz!";
}
@GetMapping("/add")
public String add() {
ScheduleJob job = new ScheduleJob();
job.setJobName("任務(wù)02");
job.setCronExpression("0/2 * * * * ?");
job.setBeanName("testJob02");
job.setMethodName("execute");
jobService.add(job);
return "新增定時(shí)任務(wù)成功";
}
@GetMapping("/start/{id}")
public String start(@PathVariable("id") Integer id) {
jobService.start(id);
return "啟動(dòng)定時(shí)任務(wù)成功";
}
@GetMapping("/pause/{id}")
public String pause(@PathVariable("id") Integer id) {
jobService.pause(id);
return "暫停定時(shí)任務(wù)成功";
}
@GetMapping("/delete/{id}")
public String delete(@PathVariable("id") Integer id) {
jobService.delete(id);
return "刪除定時(shí)任務(wù)成功";
}
@GetMapping("/startAllJob")
public String startAllJob() {
jobService.startAllJob();
return "啟動(dòng)所有定時(shí)任務(wù)成功";
}
@GetMapping("/pauseAllJob")
public String pauseAllJob() {
jobService.pauseAllJob();
return "暫停所有定時(shí)任務(wù)成功";
}
}
使用spring自帶的@Scheduled注解實(shí)現(xiàn)的定時(shí)任務(wù)
@Component
@EnableScheduling
public class SchedulerTask {
private int count = 0;
@Scheduled(cron = "*/1 * * * * ?")
private void process() {
System.out.println("this is scheduler task runing " + (count++));
System.out.println("當(dāng)前時(shí)間:" + LocalTime.now());
}
}