Spring Boot2.x + Quartz 定时任务模块
Posted 在奋斗的大道
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring Boot2.x + Quartz 定时任务模块相关的知识,希望对你有一定的参考价值。
介绍
项目是为定时任务模块,可以作为熟悉了解定时任务框架项目也可以将本模块直接移植进自己的项目中
软件涉及技术
SpringBoot2.x + MyBatis3 +MyBatis3-Plus + mysql8 + Quartz(定时任务框架) +Swagger2(接口文档框架)
效果截图
项目整体结构:
项目重点细节说明
项目之pom.xml
<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.zzg</groupId>
<artifactId>sb-Quartz</artifactId>
<version>0.0.1-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.2.RELEASE</version>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<!-- 集中定义管理依赖版本号 -->
<commons-lang.version>2.6</commons-lang.version>
<commons-codec.version>1.10</commons-codec.version>
<commons-lang3.version>3.9</commons-lang3.version>
<commons-net.version>3.6</commons-net.version>
<commons-io.version>2.6</commons-io.version>
<commons-collections.version>3.2.1</commons-collections.version>
<commons-text.version>1.8</commons-text.version>
<common-fileupload.version>1.3.1</common-fileupload.version>
<servlet-api.version>3.1.0</servlet-api.version>
<httpclient.version>4.5.2</httpclient.version>
<fastjson.version>1.2.48</fastjson.version>
</properties>
<dependencies>
<!--starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--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>
<!-- 数据库连接池druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
<!--mysql 驱动程序 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- Swagger2 集成 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.7.0</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.7.0</version>
</dependency>
<!-- 数据库文档自动生成 -->
<dependency>
<groupId>cn.smallbun.screw</groupId>
<artifactId>screw-core</artifactId>
<version>1.0.5</version>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.30</version>
</dependency>
<!--lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!-- mybatis-plus 集成 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.1</version>
</dependency>
<!--validation -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.9.0</version>
</dependency>
<!--common-lang 常用工具包 -->
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>${commons-lang.version}</version>
</dependency>
<!--commons-lang3 工具包 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>${commons-lang3.version}</version>
</dependency>
<!--commons-codec 加密工具包 -->
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>${commons-codec.version}</version>
</dependency>
<!--commons-net 网络工具包 -->
<dependency>
<groupId>commons-net</groupId>
<artifactId>commons-net</artifactId>
<version>${commons-net.version}</version>
</dependency>
<!--common-io 工具包 -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>${commons-io.version}</version>
</dependency>
<!--common-collection 工具包 -->
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>${commons-collections.version}</version>
</dependency>
<!--common-fileupload 工具包 -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>${common-fileupload.version}</version>
</dependency>
<!-- common-text 工具包 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-text</artifactId>
<version>${commons-text.version}</version>
</dependency>
<!-- 集成servlet -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>${servlet-api.version}</version>
</dependency>
<!-- 集成Apache HttpClient -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>${httpclient.version}</version>
</dependency>
<!-- 集成fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
</dependencies>
</project>
数据库 schedule_job
CREATE TABLE `schedule_job` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '任务id',
`job_name` varchar(255) CHARACTER SET utf8 DEFAULT NULL COMMENT '任务名称',
`cron_expression` varchar(255) CHARACTER SET utf8 DEFAULT NULL COMMENT 'cron表达式',
`bean_name` varchar(255) CHARACTER SET utf8 DEFAULT NULL COMMENT '服务名称',
`method_name` varchar(255) CHARACTER SET utf8 DEFAULT NULL COMMENT '方法名称',
`status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '状态 0 启动 1 停止',
`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`del_flag` tinyint(4) NOT NULL DEFAULT '0' COMMENT '软删除标记 0 正常 1 删除',
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC COMMENT='定时任务';
schedule_job 表对应的类
com.zzg.entity.ScheduleJob
com.zzg.mapper.ScheduleJobMapper
com.zzg.service.ScheduleJobService
com.zzg.service.impl.ScheduleJobServiceImpl
ScheduleJobMapper.xml
定时任务配置对象
package com.zzg.config;
import org.quartz.Scheduler;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
/**
* 定时任务配置对象
* @author zzg
*
*/
public class QuartzConfig {
@Bean
public SchedulerFactoryBean schedulerFactoryBean(){
SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
//覆盖已存在的任务
schedulerFactoryBean.setOverwriteExistingJobs(true);
//延时60秒启动定时任务,避免系统未完全启动却开始执行定时任务的情况
schedulerFactoryBean.setStartupDelay(60);
return schedulerFactoryBean;
}
/** 创建schedule **/
@Bean(name = "scheduler")
public Scheduler scheduler() {
return schedulerFactoryBean().getScheduler();
}
}
枚举类:JobOperateEnum.java
package com.zzg.enums;
import java.io.Serializable;
public enum JobOperateEnum {
START(0, "启动"),
PAUSE(1, "暂停"),
DELETE(2, "删除");
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;
}
public String getDesc() {
return this.desc;
}
public String getEnumName() {
return name();
}
}
定时任务工厂类
package com.zzg.quartz;
import java.lang.reflect.Method;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import com.zzg.entity.ScheduleJob;
import com.zzg.util.SpringContextUtil;
public class QuartzFactory implements Job {
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
// TODO Auto-generated method stub
// 获取调度数据
ScheduleJob scheduleJob = (ScheduleJob) context.getMergedJobDataMap().get("scheduleJob");
// 获取对应的Bean
Object object = SpringContextUtil.getBean(scheduleJob.getBeanName());
try {
// 利用反射执行对应方法
Method method = object.getClass().getMethod(scheduleJob.getMethodName());
method.invoke(object);
} catch (Exception e) {
e.printStackTrace();
}
}
}
定时任务服务层 IQuartzService.java
package com.zzg.service;
import org.quartz.SchedulerException;
import com.zzg.entity.ScheduleJob;
import com.zzg.enums.JobOperateEnum;
public interface IQuartzService {
/**
* 服务器启动执行定时任务
*/
void timingTask();
/**
* 新增定时任务
* @param job 任务
*/
void addJob(ScheduleJob job);
/**
* 操作定时任务
* @param jobOperateEnum 操作枚举
* @param job 任务
*/
void operateJob(JobOperateEnum jobOperateEnum, ScheduleJob job) throws SchedulerException;
/**
* 启动所有任务
*/
void startAllJob() throws SchedulerException;
/**
* 暂停所有任务
*/
void pauseAllJob() throws SchedulerException;
}
定时任务服务层实现类 QuartzServiceImpl.java
package com.zzg.service.impl;
import java.util.List;
import org.quartz.CronScheduleBuilder;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.zzg.entity.ScheduleJob;
import com.zzg.enums.JobOperateEnum;
import com.zzg.quartz.QuartzFactory;
import com.zzg.service.IQuartzService;
import com.zzg.service.ScheduleJobService;
@Service
public class QuartzServiceImpl implements IQuartzService {
/**
* 调度器
*/
@Autowired
private Scheduler scheduler;
@Autowired
private ScheduleJobService scheduleJobService;
@Override
public void timingTask() {
// TODO Auto-generated method stub
// 查询数据库是否存在需要定时的任务
List<ScheduleJob> scheduleJobs = scheduleJobService.list();
if (scheduleJobs != null) {
scheduleJobs.forEach(this::addJob);
}
}
@Override
public void addJob(ScheduleJob job) {
try {
// 创建触发器
Trigger trigger = TriggerBuilder.newTrigger().withIdentity(job.getJobName())
.withSchedule(CronScheduleBuilder.cronSchedule(job.getCronExpression())).startNow().build();
// 创建任务
JobDetail jobDetail = JobBuilder.newJob(QuartzFactory.class).withIdentity(job.getJobName()).build();
// 传入调度的数据,在QuartzFactory中需要使用
jobDetail.getJobDataMap().put("scheduleJob", job);
// 调度作业
scheduler.scheduleJob(jobDetail, trigger);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
public void operateJob(JobOperateEnum jobOperateEnum, ScheduleJob job) throws SchedulerException {
// TODO Auto-generated method stub
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 {
// TODO Auto-generated method stub
scheduler.start();
}
@Override
public void pauseAllJob() throws SchedulerException {
// TODO Auto-generated method stub
scheduler.standby();
}
}
JobSchedule.java 实现开机自运行
package com.zzg.quartz.components;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import com.zzg.service.IQuartzService;
/**
* 项目启动后,后台任务自带加载运行
*
* @author zzg
*
*/
@Component
public class JobSchedule implements CommandLineRunner {
@Autowired
private IQuartzService taskService;
@Override
public void run(String... args) throws Exception {
// TODO Auto-generated method stub
System.out.println("任务调度开始==============任务调度开始");
taskService.timingTask();
System.out.println("任务调度结束==============任务调度结束");
}
}
动态定时任务功能实现
静态部分
顾名思义,静态部分就是写一个方法,数据库录入一条数据,当项目启动的时候,就会触发定时任务,等到了时间就会运行。
package com.zzg.quartz.job;
import java.util.Date;
import org.springframework.stereotype.Component;
@Component("JobTest")
public class LogTest {
public void log() {
System.out.println("-------------------Log任务执行开始-------------------");
System.out.println(new Date().toLocaleString());
System.out.println("-------------------Log任务执行结束-------------------");
}
}
数据库表 schedule_job
执行效果
动态部分
动态部分就是在静态部分的基础上,加入controller,直接通过接口来实时操作定时任务
package com.zzg.controller;
import org.quartz.SchedulerException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.zzg.common.Result;
import com.zzg.entity.ScheduleJob;
import com.zzg.enums.JobOperateEnum;
import com.zzg.service.IQuartzService;
import com.zzg.service.ScheduleJobService;
import io.swagger.annotations.ApiOperation;
@RestController
@RequestMapping("/api/quartz")
public class QuartzController {
@Autowired
private ScheduleJobService scheduleJobService;
@Autowired
private IQuartzService quartzService;
@PostMapping("/startAllJob")
@ApiOperation("开始所有定时任务")
public Result startAllJob() throws SchedulerException {
quartzService.startAllJob();
return Result.ok();
}
@PostMapping("/pauseAllJob")
@ApiOperation("暂停所有定时任务")
public Result pauseAllJob() throws SchedulerException {
quartzService.pauseAllJob();
return Result.ok();
}
@PostMapping("/startJob")
@ApiOperation("启动定时任务")
public Result start(@RequestBody ScheduleJob form) {
try {
quartzService.operateJob(JobOperateEnum.START,form);
// 补充业务逻辑
ScheduleJob entity = scheduleJobService.getById(form.getId());
entity.setStatus(0);
scheduleJobService.saveOrUpdate(entity);
} catch (SchedulerException e) {
// 堆栈异常输出
e.printStackTrace();
return Result.error("启动定时任务失败");
}
return Result.ok();
}
@PostMapping("/pauseJob")
@ApiOperation("暂停定时任务")
public Result pause(@RequestBody ScheduleJob form) {
try {
quartzService.operateJob(JobOperateEnum.PAUSE, form);
// 补充业务逻辑
ScheduleJob entity = scheduleJobService.getById(form.getId());
entity.setStatus(1);
scheduleJobService.saveOrUpdate(entity);
} catch (SchedulerException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return Result.error("暂停定时任务失败");
}
return Result.ok();
}
@PostMapping("/deleteJob")
@ApiOperation("删除定时任务")
public Result delete(@RequestBody ScheduleJob form) {
try {
quartzService.operateJob(JobOperateEnum.DELETE, form);
// 补充其他业务逻辑
scheduleJobService.removeById(form.getId());
} catch (SchedulerException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return Result.error("删除定时任务失败");
}
return Result.ok();
}
}
知识拓展:CronTrigger配置完整格式
[秒] [分] [小时] [日] [月] [周] [年]
参数
- 秒(0~59)
- 分钟(0~59)
- 小时(0~23)
- 天(0~31,但是你需要考虑你月的天数)
- 月(0~11)
- 周(1~7)
- 年份(1970-2099)(可省略)
eg:
0 0 0/1 * * ? // 每小时执行一次
0 0 12 * * ? // 每天12点触发
0 0 22 L * ? // 每月最后一天的22点触发
源码下载地址:
百度网盘下载地址及其提取码(包含项目源码+项目初始化脚本):
链接:https://pan.baidu.com/s/19rqjrBvRX6PNbcSuyTU5BQ
提取码:1234
以上是关于Spring Boot2.x + Quartz 定时任务模块的主要内容,如果未能解决你的问题,请参考以下文章
Spring Boot2.x 的Druid连接池配置[附带监控]
spring cloudspring boot2.x下 使用feign,注解@EnableFeignClients 找不到的解决方法