【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…...
在rocky linux 9.5上在线安装 docker
前面是指南,后面是日志 sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo sudo dnf install docker-ce docker-ce-cli containerd.io -y docker version sudo systemctl start docker sudo systemctl status docker …...

Swift 协议扩展精进之路:解决 CoreData 托管实体子类的类型不匹配问题(下)
概述 在 Swift 开发语言中,各位秃头小码农们可以充分利用语法本身所带来的便利去劈荆斩棘。我们还可以恣意利用泛型、协议关联类型和协议扩展来进一步简化和优化我们复杂的代码需求。 不过,在涉及到多个子类派生于基类进行多态模拟的场景下,…...
蓝桥杯 2024 15届国赛 A组 儿童节快乐
P10576 [蓝桥杯 2024 国 A] 儿童节快乐 题目描述 五彩斑斓的气球在蓝天下悠然飘荡,轻快的音乐在耳边持续回荡,小朋友们手牵着手一同畅快欢笑。在这样一片安乐祥和的氛围下,六一来了。 今天是六一儿童节,小蓝老师为了让大家在节…...
MVC 数据库
MVC 数据库 引言 在软件开发领域,Model-View-Controller(MVC)是一种流行的软件架构模式,它将应用程序分为三个核心组件:模型(Model)、视图(View)和控制器(Controller)。这种模式有助于提高代码的可维护性和可扩展性。本文将深入探讨MVC架构与数据库之间的关系,以…...
vue3 字体颜色设置的多种方式
在Vue 3中设置字体颜色可以通过多种方式实现,这取决于你是想在组件内部直接设置,还是在CSS/SCSS/LESS等样式文件中定义。以下是几种常见的方法: 1. 内联样式 你可以直接在模板中使用style绑定来设置字体颜色。 <template><div :s…...

《通信之道——从微积分到 5G》读书总结
第1章 绪 论 1.1 这是一本什么样的书 通信技术,说到底就是数学。 那些最基础、最本质的部分。 1.2 什么是通信 通信 发送方 接收方 承载信息的信号 解调出其中承载的信息 信息在发送方那里被加工成信号(调制) 把信息从信号中抽取出来&am…...

页面渲染流程与性能优化
页面渲染流程与性能优化详解(完整版) 一、现代浏览器渲染流程(详细说明) 1. 构建DOM树 浏览器接收到HTML文档后,会逐步解析并构建DOM(Document Object Model)树。具体过程如下: (…...

Python爬虫(一):爬虫伪装
一、网站防爬机制概述 在当今互联网环境中,具有一定规模或盈利性质的网站几乎都实施了各种防爬措施。这些措施主要分为两大类: 身份验证机制:直接将未经授权的爬虫阻挡在外反爬技术体系:通过各种技术手段增加爬虫获取数据的难度…...
.Net Framework 4/C# 关键字(非常用,持续更新...)
一、is 关键字 is 关键字用于检查对象是否于给定类型兼容,如果兼容将返回 true,如果不兼容则返回 false,在进行类型转换前,可以先使用 is 关键字判断对象是否与指定类型兼容,如果兼容才进行转换,这样的转换是安全的。 例如有:首先创建一个字符串对象,然后将字符串对象隐…...

九天毕昇深度学习平台 | 如何安装库?
pip install 库名 -i https://pypi.tuna.tsinghua.edu.cn/simple --user 举个例子: 报错 ModuleNotFoundError: No module named torch 那么我需要安装 torch pip install torch -i https://pypi.tuna.tsinghua.edu.cn/simple --user pip install 库名&#x…...