SpringBoot中使用Quartz管理定時(shí)任務(wù)
點(diǎn)擊上方“ java1234?”,選擇“標(biāo)星公眾號”
優(yōu)質(zhì)文章,第一時(shí)間送達(dá)
66套java從入門到精通實(shí)戰(zhàn)課程分享
定時(shí)任務(wù)在系統(tǒng)中用到的地方很多,例如每晚凌晨的數(shù)據(jù)備份,每小時(shí)獲取第三方平臺的 Token 信息等等,之前我們都是在項(xiàng)目中規(guī)定這個(gè)定時(shí)任務(wù)什么時(shí)候啟動(dòng),到時(shí)間了便會自己啟動(dòng),那么我們想要停止這個(gè)定時(shí)任務(wù)的時(shí)候,就需要去改動(dòng)代碼,還得啟停服務(wù)器,這是非常不友好的事情
直至遇見 Quartz,利用圖形界面可視化管理定時(shí)任務(wù),使得我們對定時(shí)任務(wù)的管理更加方便,快捷
一、Quartz 簡介
Quartz是一個(gè)開源的作業(yè)調(diào)度框架,它完全由Java寫成,并設(shè)計(jì)用于J2SE和J2EE應(yīng)用中。它提供了巨大的靈 活性而不犧牲簡單性。你能夠用它來為執(zhí)行一個(gè)作業(yè)而創(chuàng)建簡單的或復(fù)雜的調(diào)度。它有很多特征,如:數(shù)據(jù)庫支持,集群,插件,EJB作業(yè)預(yù)構(gòu) 建,JavaMail及其它,支持cron-like表達(dá)式等等。
二、開發(fā)前戲
1、引入 maven 依賴
<dependency>
????<groupId>org.springframework.bootgroupId>
????<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
????<groupId>org.springframework.bootgroupId>
????<artifactId>spring-boot-starter-quartzartifactId>
dependency>這里引入了 web 的依賴,以及 Quartz 的依賴,其余依賴請根據(jù)需求自行引入
2、創(chuàng)建數(shù)據(jù)表
數(shù)據(jù)模型:
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?'任務(wù)類名',
???cron_expression varchar(32) comment?'cron表達(dá)式',
???param varchar(32) comment?'參數(shù)',
???descript varchar(11) comment?'描述',
???quartz_status varchar(255) comment?'啟動(dòng)狀態(tài)(0--啟動(dòng)1--停止)',
???create_time datetime comment?'創(chuàng)建時(shí)間',
???create_user bigint(20) comment?'創(chuàng)建人',
???status???????????????tinyint(1) default?0?comment?'狀態(tài)(0--正常1--停用)',
???del_flag tinyint(1) default?0?comment?'刪除狀態(tài)(0,正常,1已刪除)',
???primary key?(id)
)
type?= InnoDB;
alter?table?sys_quartz comment?'定時(shí)任務(wù)信息表';三、開發(fā)進(jìn)行中
1、創(chuàng)建實(shí)體類
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;
/**
?*
?* 定時(shí)任務(wù)信息表
?*
?*
?* @author lizhou
?* @since 2020-07-21
?*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("sys_quartz")
@ApiModel(value="SysQuartz對象", description="定時(shí)任務(wù)信息表")
public?class?SysQuartz extends?Model {
????@ApiModelProperty(value = "主鍵id")
????@TableId(value = "id", type?= IdType.AUTO)
????private?Long id;
????@ApiModelProperty(value = "任務(wù)類名")
????@TableField("class_name")
????private?String?className;
????@ApiModelProperty(value = "cron表達(dá)式")
????@TableField("cron_expression")
????private?String?cronExpression;
????@ApiModelProperty(value = "參數(shù)")
????@TableField("param")
????private?String?param;
????@ApiModelProperty(value = "描述")
????@TableField("descript")
????private?String?descript;
????@ApiModelProperty(value = "啟動(dòng)狀態(tài)(0--啟動(dòng)1--停止)")
????@TableField("quartz_status")
????private?Integer quartzStatus;
????@ApiModelProperty(value = "狀態(tài)(0--正常1--停用)")
????@TableField("status")
????private?Integer status;
????@ApiModelProperty(value = "刪除狀態(tài)(0--未刪除1--已刪除)")
????@TableField("del_flag")
????@TableLogic
????private?Integer delFlag;
????@ApiModelProperty(value = "創(chuàng)建者")
????@TableField("create_user")
????private?Long createUser;
????@ApiModelProperty(value = "創(chuàng)建時(shí)間")
????@TableField("create_time")
????private?String?createTime;
????@Override
????protected?Serializable pkVal() {
????????return?this.id;
????}
} 2、實(shí)現(xiàn)定時(shí)任務(wù)的 CRUD
下面我們就要完成定時(shí)任務(wù)的 新增、修改、刪除、啟停 等基本操作了,由于不是很復(fù)雜,這里的代碼就不貼出來了,貼幾張圖吧
列表頁:
新增頁:
四、定時(shí)任務(wù)
1、定時(shí)任務(wù)類
我們把定時(shí)任務(wù)都放在 job 包下面,一個(gè)定時(shí)任務(wù)就是一個(gè)文件,寫一個(gè)測試的類 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
?* 測試定時(shí)任務(wù)
?* @Author Lizhou
?* @Date 2020-07-21 10:58:58
?**/
@Slf4j
public class TestJob implements Job {
????@Override
????public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
????????System.out.println("定時(shí)任務(wù)啟動(dòng):" + DateUtils.getYmdHms());
????}
}TestJob 這個(gè)類實(shí)現(xiàn)了 Job 接口,實(shí)現(xiàn)了 execute 方法,這里還可以接收參數(shù)
這個(gè)文件在 com.zyxx.sbm.job 包下面,那么在頁面新增定時(shí)任務(wù)的時(shí)候,就需要填寫任務(wù)類名為:com.zyxx.sbm.job.TestJob
cron 表達(dá)式的知識這里就不一一介紹了
2、頁面添加定時(shí)任務(wù)

那么我們的任務(wù)類名就是:com.zyxx.sbm.job.TestJob
cron 表達(dá)式:*/2 * * * * ?,表示兩秒鐘執(zhí)行一次
參數(shù):我們沒有傳入?yún)?shù)
3、后臺添加定時(shí)任務(wù)
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;
/**
?*
?* 定時(shí)任務(wù)信息表 服務(wù)實(shí)現(xiàn)類
?*
?*
?* @author?lizhou
?* @since?2020-07-21
?*/
@Slf4j
@Service
public?class?SysQuartzServiceImpl?extends?ServiceImpl<SysQuartzMapper, SysQuartz> implements?SysQuartzService?{
????@Autowired
????private?Scheduler scheduler;
??/**
??* 添加定時(shí)任務(wù)
??*/
????@Override
????public?ResponseResult add(SysQuartz sysQuartz)?{
????????QueryWrapper queryWrapper = new?QueryWrapper<>();
????????queryWrapper.eq("class_name", sysQuartz.getClassName());
????????List sysQuartzList = list(queryWrapper);
????????if?(null?!= sysQuartzList && !sysQuartzList.isEmpty()) {
????????????return?ResponseResult.getInstance().error("該任務(wù)類名已經(jīng)存在");
????????}
????????sysQuartz.setCreateTime(DateUtils.getYmdHms());
????????sysQuartz.setCreateUser(SingletonLoginUtils.getUserId());
????????save(sysQuartz);
????????// 啟動(dòng)
????????if?(0?== sysQuartz.getQuartzStatus()) {
????????????this.schedulerAdd(sysQuartz.getClassName().trim(), sysQuartz.getCronExpression().trim(), sysQuartz.getParam());
????????}
????????return?ResponseResult.getInstance().success();
????}
????/**
?????* 添加定時(shí)任務(wù)
?????*
?????* @param?className
?????* @param?cronExpression
?????* @param?param
?????*/
????@Override
????public?void?schedulerAdd(String className, String cronExpression, String param)?{
????????try?{
????????????// 啟動(dòng)調(diào)度器
????????????scheduler.start();
????????????// 構(gòu)建job信息
????????????JobDetail jobDetail = JobBuilder.newJob(getClass(className).getClass()).withIdentity(className).usingJobData("param", param).build();
????????????// 表達(dá)式調(diào)度構(gòu)建器(即任務(wù)執(zhí)行的時(shí)間)
????????????CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression);
????????????// 按新的cronExpression表達(dá)式構(gòu)建一個(gè)新的trigger
????????????CronTrigger 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());
????????}
????}
????/**
?????* 刪除定時(shí)任務(wù)
?????*
?????* @param?className
?????*/
????@Override
????public?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 對象,使用該對象開啟或停止定時(shí)任務(wù)
在啟動(dòng)定時(shí)任務(wù)之前,我們應(yīng)先刪除該任務(wù)類名開啟的定時(shí)任務(wù),防止該任務(wù)類名已經(jīng)添加過了
// 刪除定時(shí)任務(wù)
schedulerDelete(sysQuartz.getClassName().trim());
// 添加定時(shí)任務(wù)
schedulerAdd(sysQuartz.getClassName().trim(), sysQuartz.getCronExpression().trim(), sysQuartz.getParam());添加定時(shí)任務(wù),傳入任務(wù)類名,cron 表達(dá)式,參數(shù)
停止定時(shí)任務(wù),只需要:
scheduler.pauseJob(JobKey.jobKey(sysQuartz.getClassName().trim()));根據(jù)任務(wù)類名,停止定時(shí)任務(wù)即可
五、開發(fā)測試
啟動(dòng)項(xiàng)目,在管理界面,開啟定時(shí)任務(wù),即可在控制臺看到打印的信息
表示我們的定時(shí)任務(wù)已經(jīng)啟動(dòng)成功了
六、優(yōu)化建議
當(dāng)我們添加了定時(shí)任務(wù)并啟動(dòng)后,重新啟動(dòng)項(xiàng)目的時(shí)候,定時(shí)任務(wù)卻不會自動(dòng)啟動(dòng),這時(shí)候,我們就需要在項(xiàng)目啟動(dòng)的時(shí)候做一些事情了,也就是系統(tǒng)啟動(dòng)任務(wù)
不清楚的同學(xué)可以復(fù)習(xí)一下之前我的博客【SpringBoot】十九、SpringBoot中實(shí)現(xiàn)啟動(dòng)任務(wù)
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
?* 項(xiàng)目啟動(dòng)任務(wù)--啟動(dòng)定時(shí)任務(wù)
?* @Author?Lizhou
?* @Date?2020-07-21 12:56:56
?**/
@Component
@Order(100)
public?class?SystemQuartzStartTask?implements?CommandLineRunner?{
????@Autowired
????private?SysQuartzService sysQuartzService;
????@Override
????public?void?run(String... args)?throws?Exception {
????????// 查詢啟動(dòng)的定時(shí)任務(wù)
????????QueryWrapper queryWrapper = new?QueryWrapper<>();
????????queryWrapper.eq("status", 0);
????????queryWrapper.eq("quartz_status", 0);
????????List list = sysQuartzService.list(queryWrapper);
????????if?(null?!= list && !list.isEmpty()) {
????????????for?(SysQuartz item : list) {
????????????????// 刪除定時(shí)任務(wù)
????????????????sysQuartzService.schedulerDelete(item.getClassName().trim());
????????????????// 添加定時(shí)任務(wù)
????????????????sysQuartzService.schedulerAdd(item.getClassName().trim(), item.getCronExpression().trim(), item.getParam());
????????????}
????????}
????}
} 從數(shù)據(jù)庫查詢出啟動(dòng)的定時(shí)任務(wù),并將他們添加到定時(shí)任務(wù)啟動(dòng)中,這樣項(xiàng)目一啟動(dòng)時(shí),就會自動(dòng)啟動(dòng)我們定義的定時(shí)任務(wù)了
最后
任務(wù)類名的正則表達(dá)式
/^[a-zA-Z]+(\.([a-zA-Z])+)+$/cron 表達(dá)式的驗(yàn)證使用正則太麻煩,可以使用 Quartz 自帶驗(yàn)證方法
CronExpression.isValidExpression(cron)SpringBoot 中使用 Quartz 管理定時(shí)任務(wù)的學(xué)習(xí)就到這兒了,其實(shí)也并不難理解,相比于之前用的定時(shí)任務(wù)是不是好很多了呢,別忘了最后加上系統(tǒng)啟動(dòng)任務(wù)哦
如您在閱讀中發(fā)現(xiàn)不足,歡迎留言!!!
版權(quán)聲明:本文為博主原創(chuàng)文章,遵循?CC 4.0 BY-SA?版權(quán)協(xié)議,轉(zhuǎn)載請附上原文出處鏈接和本聲明。
本文鏈接:
https://blog.csdn.net/qq_40065776/article/details/107489728


??? ?
感謝點(diǎn)贊支持下哈?
