SpringBoot中使用Quartz管理定時(shí)任務(wù)的方法
定時(shí)任務(wù)在系統(tǒng)中用到的地方很多,例如每晚凌晨的數(shù)據(jù)備份,每小時(shí)獲取第三方平臺的 Token 信息等等,之前我們都是在項(xiàng)目中規(guī)定這個(gè)定時(shí)任務(wù)什么時(shí)候啟動(dòng),到時(shí)間了便會(huì)自己啟動(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 依賴
<!-- web支持 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- Quartz 定時(shí)任務(wù) --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-quartz</artifactId> </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; /** * <p> * 定時(shí)任務(wù)信息表 * </p> * * @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<SysQuartz> { @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; /** * <p> * 定時(shí)任務(wù)信息表 服務(wù)實(shí)現(xiàn)類 * </p> * * @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<SysQuartz> queryWrapper = new QueryWrapper<>(); queryWrapper.eq("class_name", sysQuartz.getClassName()); List<SysQuartz> 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ù)卻不會(huì)自動(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<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) { // 刪除定時(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í),就會(huì)自動(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ù)哦
總結(jié)
到此這篇關(guān)于SpringBoot中使用Quartz管理定時(shí)任務(wù)的文章就介紹到這了,更多相關(guān)SpringBoot管理定時(shí)任務(wù)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java數(shù)據(jù)結(jié)構(gòu)及算法實(shí)例:選擇排序 Selection Sort
這篇文章主要介紹了Java數(shù)據(jù)結(jié)構(gòu)及算法實(shí)例:選擇排序 Selection Sort,本文直接給出實(shí)現(xiàn)代碼,代碼中包含詳細(xì)注釋,需要的朋友可以參考下2015-06-06解析ConcurrentHashMap: 預(yù)熱(內(nèi)部一些小方法分析)
ConcurrentHashMap是由Segment數(shù)組結(jié)構(gòu)和HashEntry數(shù)組結(jié)構(gòu)組成。Segment的結(jié)構(gòu)和HashMap類似,是一種數(shù)組和鏈表結(jié)構(gòu),今天給大家普及java面試常見問題---ConcurrentHashMap知識,一起看看吧2021-06-06java查詢近七日數(shù)據(jù)功能的實(shí)現(xiàn)
這篇文章主要介紹了java查詢近七日數(shù)據(jù)功能的實(shí)現(xiàn),文章內(nèi)容詳細(xì),簡單易懂,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2023-01-01FluentMybatis實(shí)現(xiàn)mybatis動(dòng)態(tài)sql拼裝和fluent api語法
本文主要介紹了FluentMybatis實(shí)現(xiàn)mybatis動(dòng)態(tài)sql拼裝和fluent api語法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-08-08Java程序部署到服務(wù)器上,接口請求下載文件失敗/文件為空/文件名不對的問題
這篇文章主要介紹了Java程序部署到服務(wù)器上,接口請求下載文件失敗/文件為空/文件名不對,本文給大家分享錯(cuò)誤原因及解決方法,需要的朋友可以參考下2020-07-07Java 常見異常(Runtime Exception )詳細(xì)介紹并總結(jié)
這篇文章主要介紹了Java 常見異常(Runtime Exception )詳細(xì)介紹并相關(guān)資料,大家在開發(fā)Java 應(yīng)用軟件的時(shí)候經(jīng)常會(huì)遇到各種異常這里幫大家整理了一部分,并解釋如何解決,需要的朋友可以參考下2016-10-10SpringBoot如何用java生成靜態(tài)html
這篇文章主要介紹了SpringBoot如何用java生成靜態(tài)html,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,需要的朋友可以參考一下2022-06-06使用@RequestBody傳對象參數(shù)時(shí)碰到的坑
這篇文章主要介紹了使用@RequestBody傳對象參數(shù)時(shí)碰到的坑,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-08-08