【SpringBoot17】SpringBoot中使用Quartz管理定时任务
定时任务在系统中用到的地方很多,例如每晚凌晨的数据备份,每小时获取第三方平台的 Token 信息等等,之前我们都是在项目中规定这个定时任务什么时候启动,到时间了便会自己启动,那么我们想要停止这个定时任务的时候,就需要去改动代码,还得启停服务器,这是非常不友好的事情
直至遇见 Quartz,利用图形界面可视化管理定时任务,使得我们对定时任务的管理更加方便,快捷
一、Quartz 简介
Quartz是一个开源的作业调度框架,它完全由Java写成,并设计用于J2SE和J2EE应用中。它提供了巨大的灵 活性而不牺牲简单性。你能够用它来为执行一个作业而创建简单的或复杂的调度。它有很多特征,如:数据库支持,集群,插件,EJB作业预构 建,JavaMail及其它,支持cron-like表达式等等。
二、开发准备
1、引入 maven 依赖
<!-- 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>
这里引入了 web 的依赖,以及 Quartz 的依赖,其余依赖请根据需求自行引入
2、创建数据表
数据模型:
SQL语句:
drop table if exists sys_quartz;/*==============================================================*/
/* Table: sys_quartz */
/*==============================================================*/
create table sys_quartz
(id bigint(20) not null auto_increment comment '主键id',class_name varchar(32) comment '任务类名',cron_expression varchar(32) comment 'cron表达式',param varchar(32) comment '参数',descript varchar(11) comment '描述',quartz_status varchar(255) comment '启动状态(0--启动1--停止)',create_time datetime comment '创建时间',create_user bigint(20) comment '创建人',status tinyint(1) default 0 comment '状态(0--正常1--停用)',del_flag tinyint(1) default 0 comment '删除状态(0,正常,1已删除)',primary key (id)
)
type = InnoDB;alter table sys_quartz comment '定时任务信息表';
三、开发进行中
1、创建实体类
import com.baomidou.mybatisplus.annotation.*;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import com.zyxx.common.annotation.Dict;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;import java.io.Serializable;/*** <p>* 定时任务信息表* </p>** @author lizhou* @since 2020-07-21*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("sys_quartz")
@ApiModel(value="SysQuartz对象", description="定时任务信息表")
public class SysQuartz extends Model<SysQuartz> {@ApiModelProperty(value = "主键id")@TableId(value = "id", type = IdType.AUTO)private Long id;@ApiModelProperty(value = "任务类名")@TableField("class_name")private String className;@ApiModelProperty(value = "cron表达式")@TableField("cron_expression")private String cronExpression;@ApiModelProperty(value = "参数")@TableField("param")private String param;@ApiModelProperty(value = "描述")@TableField("descript")private String descript;@ApiModelProperty(value = "启动状态(0--启动1--停止)")@TableField("quartz_status")private Integer quartzStatus;@ApiModelProperty(value = "状态(0--正常1--停用)")@TableField("status")private Integer status;@ApiModelProperty(value = "删除状态(0--未删除1--已删除)")@TableField("del_flag")@TableLogicprivate Integer delFlag;@ApiModelProperty(value = "创建者")@TableField("create_user")private Long createUser;@ApiModelProperty(value = "创建时间")@TableField("create_time")private String createTime;@Overrideprotected Serializable pkVal() {return this.id;}
}
2、实现定时任务的 CRUD
下面我们就要完成定时任务的 新增、修改、删除、启停 等基本操作了,由于不是很复杂,这里的代码就不贴出来了,贴几张图吧
列表页:
四、定时任务
1、定时任务类
我们把定时任务都放在 job 包下面,一个定时任务就是一个文件,写一个测试的类 TestJob.java
import com.zyxx.common.utils.DateUtils;
import lombok.extern.slf4j.Slf4j;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;/*** @ClassName TestJob* 测试定时任务* @Author * @Date 2020-07-21 10:58:58**/
@Slf4j
public class TestJob implements Job {@Overridepublic void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {System.out.println("定时任务启动:" + DateUtils.getYmdHms());}
}
TestJob 这个类实现了 Job 接口,实现了 execute 方法,这里还可以接收参数
这个文件在 com.zyxx.sbm.job 包下面,那么在页面新增定时任务的时候,就需要填写任务类名为:com.zyxx.sbm.job.TestJob
cron 表达式的知识这里就不一一介绍了
2、页面添加定时任务
那么我们的任务类名就是:com.zyxx.sbm.job.TestJob
cron 表达式:*/2 * * * * ?,表示两秒钟执行一次
参数:我们没有传入参数
3、后台添加定时任务
package com.zyxx.sbm.service.impl;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zyxx.common.shiro.SingletonLoginUtils;
import com.zyxx.common.utils.DateUtils;
import com.zyxx.common.utils.LayTableResult;
import com.zyxx.common.utils.ResponseResult;
import com.zyxx.sbm.entity.SysQuartz;
import com.zyxx.sbm.mapper.SysQuartzMapper;
import com.zyxx.sbm.service.SysQuartzService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.quartz.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.util.List;/*** <p>* 定时任务信息表 服务实现类* </p>** @author * @since 2020-07-21*/
@Slf4j
@Service
public class SysQuartzServiceImpl extends ServiceImpl<SysQuartzMapper, SysQuartz> implements SysQuartzService {@Autowiredprivate Scheduler scheduler;/*** 添加定时任务*/@Overridepublic ResponseResult add(SysQuartz sysQuartz) {QueryWrapper<SysQuartz> queryWrapper = new QueryWrapper<>();queryWrapper.eq("class_name", sysQuartz.getClassName());List<SysQuartz> sysQuartzList = list(queryWrapper);if (null != sysQuartzList && !sysQuartzList.isEmpty()) {return ResponseResult.getInstance().error("该任务类名已经存在");}sysQuartz.setCreateTime(DateUtils.getYmdHms());sysQuartz.setCreateUser(SingletonLoginUtils.getUserId());save(sysQuartz);// 启动if (0 == sysQuartz.getQuartzStatus()) {this.schedulerAdd(sysQuartz.getClassName().trim(), sysQuartz.getCronExpression().trim(), sysQuartz.getParam());}return ResponseResult.getInstance().success();}/*** 添加定时任务** @param className* @param cronExpression* @param param*/@Overridepublic void schedulerAdd(String className, String cronExpression, String param) {try {// 启动调度器scheduler.start();// 构建job信息JobDetail jobDetail = JobBuilder.newJob(getClass(className).getClass()).withIdentity(className).usingJobData("param", param).build();// 表达式调度构建器(即任务执行的时间)CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression);// 按新的cronExpression表达式构建一个新的triggerCronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(className).withSchedule(scheduleBuilder).build();scheduler.scheduleJob(jobDetail, trigger);} catch (SchedulerException e) {log.error(e.getMessage());} catch (RuntimeException e) {log.error(e.getMessage());} catch (Exception e) {log.error(e.getMessage());}}/*** 删除定时任务** @param className*/@Overridepublic void schedulerDelete(String className) {try {scheduler.pauseTrigger(TriggerKey.triggerKey(className));scheduler.unscheduleJob(TriggerKey.triggerKey(className));scheduler.deleteJob(JobKey.jobKey(className));} catch (Exception e) {log.error(e.getMessage(), e);}}private static Job getClass(String className) throws Exception {Class<?> class1 = Class.forName(className);return (Job) class1.newInstance();}
}
需要注入 Scheduler 对象,使用该对象开启或停止定时任务
在启动定时任务之前,我们应先删除该任务类名开启的定时任务,防止该任务类名已经添加过了
// 删除定时任务
schedulerDelete(sysQuartz.getClassName().trim());
// 添加定时任务
schedulerAdd(sysQuartz.getClassName().trim(), sysQuartz.getCronExpression().trim(), sysQuartz.getParam());
添加定时任务,传入任务类名,cron 表达式,参数
停止定时任务,只需要:
scheduler.pauseJob(JobKey.jobKey(sysQuartz.getClassName().trim()));
根据任务类名,停止定时任务即可
五、开发测试
启动项目,在管理界面,开启定时任务,即可在控制台看到打印的信息
表示我们的定时任务已经启动成功了
六、优化建议
当我们添加了定时任务并启动后,重新启动项目的时候,定时任务却不会自动启动,这时候,我们就需要在项目启动的时候做一些事情了,也就是系统启动任务
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.zyxx.sbm.entity.SysQuartz;
import com.zyxx.sbm.service.SysQuartzService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;import java.util.List;/*** @ClassName SystemStartTask* 项目启动任务--启动定时任务* @Author * @Date 2020-07-21 12:56:56**/
@Component
@Order(100)
public class SystemQuartzStartTask implements CommandLineRunner {@Autowiredprivate SysQuartzService sysQuartzService;@Overridepublic void run(String... args) throws Exception {// 查询启动的定时任务QueryWrapper<SysQuartz> queryWrapper = new QueryWrapper<>();queryWrapper.eq("status", 0);queryWrapper.eq("quartz_status", 0);List<SysQuartz> list = sysQuartzService.list(queryWrapper);if (null != list && !list.isEmpty()) {for (SysQuartz item : list) {// 删除定时任务sysQuartzService.schedulerDelete(item.getClassName().trim());// 添加定时任务sysQuartzService.schedulerAdd(item.getClassName().trim(), item.getCronExpression().trim(), item.getParam());}}}
}
从数据库查询出启动的定时任务,并将他们添加到定时任务启动中,这样项目一启动时,就会自动启动我们定义的定时任务了
最后
任务类名的正则表达式
/^[a-zA-Z]+(\.([a-zA-Z])+)+$/
cron 表达式的验证使用正则太麻烦,可以使用 Quartz 自带验证方法
CronExpression.isValidExpression(cron)
SpringBoot 中使用 Quartz 管理定时任务的学习就到这儿了,其实也并不难理解,相比于之前用的定时任务是不是好很多了呢,别忘了最后加上系统启动任务哦
相关文章:

【SpringBoot17】SpringBoot中使用Quartz管理定时任务
定时任务在系统中用到的地方很多,例如每晚凌晨的数据备份,每小时获取第三方平台的 Token 信息等等,之前我们都是在项目中规定这个定时任务什么时候启动,到时间了便会自己启动,那么我们想要停止这个定时任务的时候&…...

杨辉三角形 (蓝桥杯) JAVA
目录题目描述:暴力破解(四成):二分法破解(满分):题目描述: 下面的图形是著名的杨辉三角形: 如果我们按从上到下、从左到右的顺序把所有数排成一列,可以得到如…...

AI制药 - AlphaFold Multimer 的 MSA Pairing 源码
目前最新版本是v2.3.1,2023.1.12 AlphaFold multimer v1 于 2021 年 7 月发布,同时发表了一篇描述其方法和结果的论文。AlphaFold multimer v1 使用了与 AlphaFold 单体相同的模型结构和训练方法,但增加了一些特征和损失函数来处理多条链。Al…...

TitanIDE:云原生开发到底强在哪里?
原文作者:行云创新技术总监 邓冰寒 引言 是一种新的软件开发方法,旨在构建更可靠、高效、弹性、安全和可扩展的应用程序。与传统的应用程序开发方式不同,云原生是将开发环境完全搬到云端,构建一站式的云原生开发环境。云原生的开…...

单片机常用完整性校验算法
一、前言 单片机在开发过程中经常会遇到大文件传输,或者大量数据传输,在一些工业环境下,数据传输并不是很稳定,如何检验数据的完整性就是个问题,这里简单介绍一下单片机常用的几种数据完整性校验方法。 二、CheckSum校…...

Anaconda 的安装配置及依赖项的内外网配置
在分享anaconda 的安装配置及使用前,我们必须先明白anaconda是什么;Anaconda是一个开源的Python发行版本。两者区别在于前者是一门编程语言,后者相当于编程语言中的工具包。 由于python自身缺少numpy、matplotlib、scipy、scikit-learn等一系…...

p84 CTF夺旗-PHP弱类型异或取反序列化RCE
数据来源 文章参考 本课重点: 案例1:PHP-相关总结知识点-后期复现案例2:PHP-弱类型对比绕过测试-常考点案例3:PHP-正则preg_match绕过-常考点案例4:PHP-命令执行RCE变异绕过-常考点案例5:PHP-反序列化考题…...

2022财报逆转,有赞穿透迷雾实现突破
2022年,商家经营面临困难。但在一些第三方服务商的帮助下,也有商家取得了逆势增长。 2023年3月23日,有赞发布2022年业绩报告,它帮助许多商家稳住了一整年的经营。2022年,有赞门店SaaS业务的GMV达到425亿元,…...

蓝桥杯 - 求组合数【C(a,b)】+ 卡特兰数
文章目录💬前言885. 求组合数 I C(m,n) 【dp】886 求组合数 II 【数据大小10万级别】 【费马小定理快速幂逆元】887. 求组合数 III 【le18级别】 【卢卡斯定理 逆元 快速幂 】888.求组合数 IV 【没有%p -- 高精度算出准确结果】 【分解质因数 高精度乘法 --只用一…...

膳食真菌在癌症免疫治疗中的作用: 从肠道微生物群的角度
谷禾健康 癌症是一种恶性肿瘤,它可以发生在人体的任何部位,包括肺、乳房、结肠、胃、肝、宫颈等。根据世界卫生组织的数据,全球每年有超过1800万人被诊断出患有癌症,其中约有1000万人死于癌症。癌症已成为全球范围内的主要健康问题…...

怎么将模糊的照片变清晰
怎么将模糊的照片变清晰?珍贵的照片每个人都会有,而遇到珍贵的照片变模糊了,相信会让人很苦恼的。那么有没有办法可以解决呢?答案是有的,我们可以用工具让模糊的照片变得清晰。下面就来分享一些让模糊的照片变清晰的方法,有兴趣…...

【软件测试】基础知识第一篇
文章目录一. 什么是软件测试二. 测试和调试的区别三. 什么是测试用例四. 软件的生命周期五. 软件测试的生命周期一. 什么是软件测试 软件测试就是验证软件产品特性是否满足用户的需求。 那需求又是什么呢?在多数软件公司,会有两种需求,一种…...

【百面成神】java web基础7问,你能坚持到第几问
前 言 🍉 作者简介:半旧518,长跑型选手,立志坚持写10年博客,专注于java后端 ☕专栏简介:纯手打总结面试题,自用备用 🌰 文章简介:java web最基础、重要的8道面试题 文章目…...

Centos7安装、各种环境配置和常见bug解决方案,保姆级教程(更新中)
文章目录前言一、Centos7安装二、各种环境配置与安装2.1 安装net-tools(建议)2.2 配置静态网络(建议)2.1 修改Centos7的时间(建议)2.2 Centos7系统编码问题2.3 vim安装(建议)2.4 解决…...

【C++进阶】智能指针
文章目录为什么需要智能指针?内存泄漏什么是内存泄漏,内存泄漏的危害内存泄漏分类(了解)如何避免内存泄漏智能指针的使用及原理smart_ptrauto_ptrunique_ptrshared_ptr线程安全的解决循环引用weak_ptr删除器为什么需要智能指针&am…...

软件测试面试题 —— 整理与解析(3)
😏作者简介:博主是一位测试管理者,同时也是一名对外企业兼职讲师。 📡主页地址:🌎【Austin_zhai】🌏 🙆目的与景愿:旨在于能帮助更多的测试行业人员提升软硬技能…...

springboot常用的20个注解
Spring Boot方式的项目开发已经逐步成为Java应用开发领域的主流框架,它不仅可以方便地创建生产级的Spring应用程序,还能轻松地通过一些注解配置与目前比较火热的微服务框架SpringCloud集成, 而Spring Boot 之所以能够轻松地实现应的创建及与…...

USB组合设备——带鼠标功能的键盘
文章目录带鼠标功能的键盘一个接口实现报告描述符示例多个接口实现复合设备和组合设备配置描述符集合的实现报告的返回附 STM32 枚举日志复合设备:Compound Device 内嵌 Hub 和多个 Function,每个 Function 都相当于一个独立的 USB 外设,有自…...

数据结构与算法基础-学习-18-哈夫曼编码
一、个人理解在远程通讯中,需要把字符转成二进制的字符串进行传输,例如我们需要传输ABCD,我们可以用定长的字符串进行表示,例如:A:00B:01C:02D:03这样可能就造成空间的浪费,我们多存储了一个0号位。那用变长呢…...

ZMC408CE | 实现“8通道独立PSO”应用场景
一、ZMC408SCAN产品亮点 1.高性能处理器,提升运算速度、响应时间和扫描周期等; 2.一维/二维/三维、多通道视觉飞拍,高速高精; 3.位置同步输出PSO,连续轨迹加工中对精密点胶胶量控制和激光能量控制等; 4…...

QuickJS中JS_SetClassProto方法把JavaScript对象指定为某个类的原型对象
在 QuickJS 中,JS_SetClassProto 方法用于设置一个类的原型对象。这个方法的作用是将一个 JavaScript 对象指定为该类的原型对象,从而定义该类的属性和方法。 具体来说,JS_SetClassProto 方法的第一个参数是指向 QuickJS 引擎执行上下文的指…...

泰克信号发生器特点
泰克信号发生器是一种用于产生各种类型的电子信号的仪器,可以广泛应用于电子、通信、自动化、医疗等领域。泰克信号发生器具有以下特点:多种信号类型:泰克信号发生器可以产生多种类型的电子信号,包括正弦波、方波、三角波、脉冲等…...

贯穿设计模式第四话--里氏替换原则
🥳🥳🥳 茫茫人海千千万万,感谢这一刻你看到了我的文章,感谢观赏,大家好呀,我是最爱吃鱼罐头,大家可以叫鱼罐头呦~🥳🥳🥳 从今天开始,将…...

6501: 鸡兔同笼
描述 一个笼子里面关了鸡和免子(鸡有两只脚,兔子有4只脚,没有例外)。已经知道了笼子里面脚的总数a,问笼子里面至少有多少只动物,至多有多少只动物。 输入 一个正整数a(a<32768)。 输出 包含两个正整数,第一个是最少的动物数,第二个是最多的…...

Linux项目自动化构建工具-make/makefile 介绍及使用
使用背景 在工程中的源文件不计数,其按类型、功能、模块分别放在若干个目录中,makefile定义一系列 规则来指定什么文件需要先编译,什么文件需要后编译,哪些文件需要重新编译,或者更复杂 的功能操作 makefile带来的好处…...

【云原生|Docker】06-dokcerfile详解
目录 前言 Dockerfile基础示例 Dockerfile简介 1. Dockerfile概念 2. Dokcer镜像分层理解 3. Doker build构建原理 Dockerfile参数解析 1. Dokcerfile组成 2. 指令说明 2.1 FROM引入基础镜像 2.2 LABEL 2.3 ENV 2.4 RUN 2.5 COPY 2.6 ADD 2…...

【SCL】博图——先入先出排序法
使用博图SCL语言来实现先入先出排序 前言 使用SCL完成一个先入先出排序 具体要求:最先输入的一个数值,最先输出出来,下面的数自动向前填充; 注:这里可能有两种理解:一是第一个输入的第一个出来ÿ…...

OSPF----特殊区域
目录 OSPF----特殊区域 第一大类----末梢区域(Stub Area) 完全末梢区域((Totally Stub Area) 第二大类特殊区域----非完全末梢区域(NSSA) OSPF----特殊区域 第一大类----末梢区域(Stub Area)…...

JVM-类加载
1:类加载机制: 加、验、准、解、初、使、卸 加、烟、准、姐、初、湿、鞋 加载、将class 文件转化为二进制流加载 JVM 内存中并生成一个该类的Class对象验证、Class 文件的字节流中包含的信息是否符合当前虚拟机的要求准备、在方法区中分配这些变量所…...

超详细讲解C语言文件操作!!
超详细讲解C语言文件操作!!什么是文件文件名文件的打开和关闭文件指针文件的打开和关闭文件的顺序读写文件的随机读写fseekftellrewind文本文件和二进制文件文件读取结束的判定文件缓冲区什么是文件 磁盘上的文件是文件。但是在程序设计中,我…...