SpringBoot?整合?Quartz?定時任務(wù)框架詳解
前言
在選擇技術(shù)棧之前,一定要先明確一件事情,你真的需要用它嗎?還有其他方式可以使用嗎?
相比其他技術(shù)技術(shù),優(yōu)點在哪里呢?使用了之后的利與弊等等。
寫這個主要是因為一直想寫一下定時任務(wù)這個主題,這個算是寫那篇文前期的鋪墊和基礎(chǔ)吧~
本文沒有聊到 Java其他的實現(xiàn)定時任務(wù)的方法啥的~,只是對使用 Quartz 做了一個小實踐
一、簡單聊一聊 Quartz
Quartz 是一個完全由 Java 編寫的開源作業(yè)調(diào)度框架,為在 Java 應(yīng)用程序中進(jìn)行作業(yè)調(diào)度提供了簡單卻強(qiáng)大的機(jī)制。
Quartz 其實就是通過一個調(diào)度線程不斷的掃描數(shù)據(jù)庫中的數(shù)據(jù)來獲取到那些已經(jīng)到點要觸發(fā)的任務(wù),然后調(diào)度執(zhí)行它的。這個線程就是 QuartzSchedulerThread類。其run方法中就是quartz的調(diào)度邏輯。
另外,這是一個Demo,木有考慮并發(fā)、多任務(wù)執(zhí)行等等狀態(tài)的發(fā)生及處理情況,見諒。
1.1、Quartz 概念
Quartz 的幾個核心概念:
- Job
表示一個工作,要執(zhí)行的具體內(nèi)容。此接口中只有一個方法
?void execute(JobExecutionContext context)
JobDetail 表示一個具體的可執(zhí)行的調(diào)度程序,Job 是這個可執(zhí)行程調(diào)度程序所要執(zhí)行的內(nèi)容,另外 JobDetail 還包含了這個任務(wù)調(diào)度的方案和策略。
Trigger 代表一個調(diào)度參數(shù)的配置,什么時候去調(diào)。
Scheduler 代表一個調(diào)度容器,一個調(diào)度容器中可以注冊多個 JobDetail 和 Trigger。當(dāng) Trigger 與 JobDetail 組合,就可以被 Scheduler 容器調(diào)度了。
二、SpringBoot 使用 Quartz
2.1、基本步驟
基本步驟就那些,這篇也不是高大上講原理和流程之類的,就是偏向?qū)嵅?,可能一些地方在代碼中含有注釋,就不再貼說明了~
基本:JDK 8、SpringBoot、MybatisPlus、Quartz
創(chuàng)建一個 SpringBoot 項目
導(dǎo)入相關(guān)依賴~
?<parent> ? ? ?<groupId>org.springframework.boot</groupId> ? ? ?<artifactId>spring-boot-starter-parent</artifactId> ? ? ?<version>2.5.2</version> ? ? ?<relativePath/> ?</parent> ?<properties> ? ? ?<java.version>1.8</java.version> ? ? ?<maven.compiler.source>8</maven.compiler.source> ? ? ?<maven.compiler.target>8</maven.compiler.target> ?</properties> ?<dependencies> ? ? ?<dependency> ? ? ? ? ?<groupId>org.springframework.boot</groupId> ? ? ? ? ?<artifactId>spring-boot-starter-web</artifactId> ? ? ?</dependency> ? ? ?<dependency> ? ? ? ? ?<groupId>org.springframework.boot</groupId> ? ? ? ? ?<artifactId>spring-boot-starter-quartz</artifactId> ? ? ?</dependency> ? ? ?<dependency> ? ? ? ? ?<groupId>org.projectlombok</groupId> ? ? ? ? ?<artifactId>lombok</artifactId> ? ? ?</dependency> ? ? ?<dependency> ? ? ? ? ?<groupId>mysql</groupId> ? ? ? ? ?<artifactId>mysql-connector-java</artifactId> ? ? ?</dependency> ? ? ?<dependency> ? ? ? ? ?<groupId>com.baomidou</groupId> ? ? ? ? ?<artifactId>mybatis-plus-boot-starter</artifactId> ? ? ? ? ?<version>3.4.1</version> ? ? ?</dependency> ? ? ?<dependency> ? ? ? ? ?<groupId>cn.hutool</groupId> ? ? ? ? ?<artifactId>hutool-all</artifactId> ? ? ? ? ?<version>5.1.4</version> ? ? ?</dependency> ? ? ?<dependency> ? ? ? ? ?<groupId>com.alibaba</groupId> ? ? ? ? ?<artifactId>fastjson</artifactId> ? ? ? ? ?<version>1.2.76</version> ? ? ?</dependency> ?</dependencies>
項目結(jié)構(gòu):

2.2、執(zhí)行 Quartz 需要的SQL文件
找到 quartz 需要的 sql 文件,在數(shù)據(jù)庫中執(zhí)行,這也是Quartz持久化的基礎(chǔ)~

往下滑,找到你需要的sql文件即可。

執(zhí)行完的結(jié)果:

在此基礎(chǔ)上,我們再額外增加一張表,與我們可能有業(yè)務(wù)關(guān)聯(lián)的信息整合,這啥啥允許為空,是方便我寫測試~,并非正例

DROP TABLE IF EXISTS `sys_quartz_job`; CREATE TABLE `sys_quartz_job` ( `id` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, `create_by` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '創(chuàng)建人', `create_time` datetime NULL DEFAULT NULL COMMENT '創(chuàng)建時間', `del_flag` int(1) UNSIGNED ZEROFILL NULL DEFAULT 0 COMMENT '刪除狀態(tài)', `update_by` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '修改人', `update_time` datetime NULL DEFAULT NULL COMMENT '修改時間', `job_class_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '任務(wù)類名', `cron_expression` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 'cron表達(dá)式', `parameter` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '參數(shù)', `description` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '描述', `status` int(1) NULL DEFAULT NULL COMMENT '狀態(tài) 0正常 -1停止', PRIMARY KEY (`id`) USING BTREE ) ENGINE = MyISAM CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = DYNAMIC;
2.3、Controller
我們直接從controller說起吧,從上往下開發(fā)。
其實一旦牽扯到表的操作,我們無疑就是crud四件事。
?/**
? * @Description: 定時任務(wù)在線管理
? * @author nzc
? */
?@RestController
?@RequestMapping("/quartzJob")
?@Slf4j
?public class QuartzJobController {
? @Autowired
? private IQuartzJobService quartzJobService;
? @Autowired
? private Scheduler scheduler;
? @RequestMapping(value = "/list", method = RequestMethod.GET)
? public Result<?> queryPageList(QuartzJob quartzJob, @RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,
? ? @RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize, HttpServletRequest req) {
? Page<QuartzJob> page = new Page<QuartzJob>(pageNo, pageSize);
? IPage<QuartzJob> pageList = quartzJobService.page(page);
? ? ? ? ?return Result.ok(pageList);
? }
? @RequestMapping(value = "/add", method = RequestMethod.POST)
? public Result<?> add(@RequestBody QuartzJob quartzJob) {
List<QuartzJob> list = quartzJobService.list(new QueryWrapper<QuartzJob>().eq("job_class_name", quartzJob.getJobClassName()));
? if (list != null && list.size() > 0) {
? return Result.error("該定時任務(wù)類名已存在");
? }
? quartzJobService.saveAndScheduleJob(quartzJob);
? return Result.ok("創(chuàng)建定時任務(wù)成功");
? }
? @RequestMapping(value = "/edit", method = RequestMethod.PUT)
? public Result<?> eidt(@RequestBody QuartzJob quartzJob) {
? try {
? quartzJobService.editAndScheduleJob(quartzJob);
? } catch (SchedulerException e) {
? log.error(e.getMessage(),e);
? return Result.error("更新定時任務(wù)失敗!");
? }
? ? ?return Result.ok("更新定時任務(wù)成功!");
? }
? @RequestMapping(value = "/delete", method = RequestMethod.DELETE)
? public Result<?> delete(@RequestParam(name = "id", required = true) String id) {
? QuartzJob quartzJob = quartzJobService.getById(id);
? if (quartzJob == null) {
? return Result.error("未找到對應(yīng)實體");
? }
? quartzJobService.deleteAndStopJob(quartzJob);
? ? ? ? ?return Result.ok("刪除成功!");
? }
? @RequestMapping(value = "/deleteBatch", method = RequestMethod.DELETE)
? public Result<?> deleteBatch(@RequestParam(name = "ids", required = true) String ids) {
? if (ids == null || "".equals(ids.trim())) {
? return Result.error("參數(shù)不識別!");
? }
? for (String id : Arrays.asList(ids.split(","))) {
? QuartzJob job = quartzJobService.getById(id);
? quartzJobService.deleteAndStopJob(job);
? }
? ? ? ? ?return Result.ok("刪除定時任務(wù)成功!");
? }
? /**
? * 暫停定時任務(wù)
? * @param jobClassName
? */
? @GetMapping(value = "/pause")
? public Result<Object> pauseJob(@RequestParam(name = "jobClassName", required = true) String jobClassName) {
? QuartzJob job = null;
? try {
? job = quartzJobService.getOne(new LambdaQueryWrapper<QuartzJob>().eq(QuartzJob::getJobClassName, jobClassName));
? if (job == null) {
? return Result.error("定時任務(wù)不存在!");
? }
? scheduler.pauseJob(JobKey.jobKey(jobClassName.trim()));
? } catch (SchedulerException e) {
? throw new MyException("暫停定時任務(wù)失敗");
? }
? job.setStatus(CommonConstant.STATUS_DISABLE);
? quartzJobService.updateById(job);
? return Result.ok("暫停定時任務(wù)成功");
? }
? /**
? * 恢復(fù)定時任務(wù)
? * @param jobClassName
? */
? @GetMapping(value = "/resume")
? public Result<Object> resumeJob(@RequestParam(name = "jobClassName", required = true) String jobClassName) {
? QuartzJob job = quartzJobService.getOne(new LambdaQueryWrapper<QuartzJob>().eq(QuartzJob::getJobClassName, jobClassName));
? if (job == null) {
? return Result.error("定時任務(wù)不存在!");
? }
? quartzJobService.resumeJob(job);
? //scheduler.resumeJob(JobKey.jobKey(job.getJobClassName().trim()));
? return Result.ok("恢復(fù)定時任務(wù)成功");
? }
? /** 通過id查詢*/
? @RequestMapping(value = "/queryById", method = RequestMethod.GET)
? public Result<?> queryById(@RequestParam(name = "id", required = true) String id) {
? QuartzJob quartzJob = quartzJobService.getById(id);
? ? ? ? ?return Result.ok(quartzJob);
? }
?}2.4、Service 劃重點
?public interface IQuartzJobService extends IService<QuartzJob> {
? boolean saveAndScheduleJob(QuartzJob quartzJob);
? boolean editAndScheduleJob(QuartzJob quartzJob) throws SchedulerException;
? boolean deleteAndStopJob(QuartzJob quartzJob);
? boolean resumeJob(QuartzJob quartzJob);
?}其中最主要的實現(xiàn)都是在這里:
?@Slf4j
?@Service
?public class QuartzJobServiceImpl extends ServiceImpl<QuartzJobMapper, QuartzJob> implements IQuartzJobService {
? @Autowired
? private QuartzJobMapper quartzJobMapper;
? @Autowired
? private Scheduler scheduler;
? /**保存&啟動定時任務(wù)*/
? @Override
? public boolean saveAndScheduleJob(QuartzJob quartzJob) {
? if (CommonConstant.STATUS_NORMAL.equals(quartzJob.getStatus())) {
? // 定時器添加
? this.schedulerAdd(quartzJob.getJobClassName().trim(), quartzJob.getCronExpression().trim(), quartzJob.getParameter());
? }
? // DB設(shè)置修改
? return this.save(quartzJob);
? }
? /**恢復(fù)定時任務(wù) */
? @Override
? public boolean resumeJob(QuartzJob quartzJob) {
? schedulerDelete(quartzJob.getJobClassName().trim());
? schedulerAdd(quartzJob.getJobClassName().trim(), quartzJob.getCronExpression().trim(), quartzJob.getParameter());
? quartzJob.setStatus(CommonConstant.STATUS_NORMAL);
? return this.updateById(quartzJob);
? }
? /**編輯&啟停定時任務(wù) @throws SchedulerException */
? @Override
? public boolean editAndScheduleJob(QuartzJob quartzJob) throws SchedulerException {
? if (CommonConstant.STATUS_NORMAL.equals(quartzJob.getStatus())) {
? schedulerDelete(quartzJob.getJobClassName().trim());
? schedulerAdd(quartzJob.getJobClassName().trim(), quartzJob.getCronExpression().trim(), quartzJob.getParameter());
? }else{
? scheduler.pauseJob(JobKey.jobKey(quartzJob.getJobClassName().trim()));
? }
? return this.updateById(quartzJob);
? }
? /**刪除&停止刪除定時任務(wù)*/
? @Override
? public boolean deleteAndStopJob(QuartzJob job) {
? schedulerDelete(job.getJobClassName().trim());
? return this.removeById(job.getId());
? }
? /** 添加定時任務(wù)*/
? private void schedulerAdd(String jobClassName, String cronExpression, String parameter) {
? try {
? // 啟動調(diào)度器
? scheduler.start();
? // 構(gòu)建job信息
? JobDetail jobDetail = JobBuilder.newJob(getClass(jobClassName).getClass()).withIdentity(jobClassName).usingJobData("parameter", parameter).build();
? // 表達(dá)式調(diào)度構(gòu)建器(即任務(wù)執(zhí)行的時間)
? CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression);
? // 按新的cronExpression表達(dá)式構(gòu)建一個新的trigger
? CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(jobClassName).withSchedule(scheduleBuilder).build();
? scheduler.scheduleJob(jobDetail, trigger);
? } catch (SchedulerException e) {
? throw new MyException("創(chuàng)建定時任務(wù)失敗", e);
? } catch (RuntimeException e) {
? throw new MyException(e.getMessage(), e);
? }catch (Exception e) {
? throw new MyException("后臺找不到該類名:" + jobClassName, e);
? }
? }
? /** 刪除定時任務(wù)*/
? private void schedulerDelete(String jobClassName) {
? try {
? /*使用給定的鍵暫停Trigger 。*/
? scheduler.pauseTrigger(TriggerKey.triggerKey(jobClassName));
? /*從調(diào)度程序中刪除指示的Trigger */
? scheduler.unscheduleJob(TriggerKey.triggerKey(jobClassName));
? /*從 Scheduler 中刪除已識別的Job - 以及任何關(guān)聯(lián)的Trigger */
? scheduler.deleteJob(JobKey.jobKey(jobClassName));
? } catch (Exception e) {
? log.error(e.getMessage(), e);
? throw new MyException("刪除定時任務(wù)失敗");
? }
? }
? private static Job getClass(String classname) throws Exception {
? Class<?> class1 = Class.forName(classname);
? return (Job) class1.newInstance();
? }
?}?@Mapper
?public interface QuartzJobMapper extends BaseMapper<QuartzJob> {
?}2.5、實體類
?@Data
?@TableName("sys_quartz_job")
?public class QuartzJob implements Serializable {
? ? @TableId(type = IdType.ID_WORKER_STR)
? ? private String id;
? ? private String createBy;
? ? private String updateBy;
? ? /**任務(wù)類名*/
? ? private String jobClassName;
? ? /** cron表達(dá)式 */
? ? private String cronExpression;
? ? /** 參數(shù)*/
? ? private String parameter;
? ? private String description;
? ? /** 狀態(tài) 0正常 -1停止*/
? ? private Integer status;
? ? @TableLogic
? ? private Integer delFlag;
? ? @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
? ? @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
? ? @TableField(fill = FieldFill.INSERT)
? private Date createTime;
? ? @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
? ? @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
? ? @TableField(fill = FieldFill.INSERT_UPDATE)
? ? private Date updateTime;
?}另外在這里順帶補充一下,本項目在yml中配置quartz,如下:
spring:
## quartz定時任務(wù),采用數(shù)據(jù)庫方式
quartz:
job-store-type: jdbc2.6、簡單的 Job 案例
如果調(diào)度器要執(zhí)行任務(wù),首先得要有一個任務(wù)相關(guān)滴類。
寫了兩個平常的案例,一個是不帶參數(shù)的,一個是帶參數(shù)的。
/**
* 不帶參的簡單定時任務(wù)
* @Author nzc
*/
@Slf4j
public class SampleJob implements Job {
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
log.info(String.format("Ning zaichun的 普通定時任務(wù) SampleJob ! 時間:" + new Date()));
}
}帶參數(shù)的:
?/**
? * 帶參數(shù)的簡單的定時任務(wù)
? * @Author nzc
? */
?@Slf4j
?public class SampleParamJob implements Job {
? /**
? * 若參數(shù)變量名修改 QuartzJobController中也需對應(yīng)修改
? */
? private String parameter;
? public void setParameter(String parameter) {
? this.parameter = parameter;
? }
? @Override
? public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
? log.info(String.format("welcome %s! Jeecg-Boot 帶參數(shù)定時任務(wù) SampleParamJob ! ? 時間:" + LocalDateTime.now(), this.parameter));
? }
?}2.7、那么該如何使用呢?
啟動項目,讓我們拿起來postman來測試吧,康康該如何使用,數(shù)據(jù)表在使用的時候,又有怎么樣的變化~
我們直接來測試添加定時任務(wù)的接口,先來個不用參數(shù)的吧

?{
? ? ?"createBy": "nzc",
? ? ?"jobClassName": "com.nzc.quartz.job.SampleJob",
? ? ?"cronExpression": "0/10 * * * * ? ",
? ? ?"description": "每十秒執(zhí)行一次",
? ? ?"status": "0"
?}添加完之后就已經(jīng)在執(zhí)行了。

此時我們將項目停止,重新啟動~

調(diào)度器也會主動去檢測任務(wù)信息,如果有,就會開始執(zhí)行。
我們再來測測帶有參數(shù)的

也是可以成功的,并且也是按照我們設(shè)定的時間執(zhí)行的

其他的接口也是同樣如此,根據(jù)接口的設(shè)定,將參數(shù)傳入即可
到此這篇關(guān)于SpringBoot 整合 Quartz 定時任務(wù)框架詳解的文章就介紹到這了,更多相關(guān)SpringBoot 整合 Quartz 內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java中@Autowired和@Resource區(qū)別
本文主要介紹了Java中@Autowired和@Resource區(qū)別,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-06-06
一些java二進(jìn)制的相關(guān)基礎(chǔ)知識
這篇文章主要介紹了一些java二進(jìn)制的相關(guān)基礎(chǔ)知識,在Java語言中byte代表最小計量單位,byte由8位2進(jìn)制數(shù)組成。,需要的朋友可以參考下2019-06-06
解決SpringBoot運行報錯:找不到或無法加載主類的問題
這篇文章主要介紹了解決SpringBoot運行報錯:找不到或無法加載主類的問題,具有很好的參考價值,對大家的學(xué)習(xí)或工作有一定的參考價值,需要的朋友可以參考下2023-09-09
使用Jenkins配置Git+Maven的自動化構(gòu)建的方法
這篇文章主要介紹了使用Jenkins配置Git+Maven的自動化構(gòu)建的方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-01-01
Java分支結(jié)構(gòu)和循環(huán)結(jié)構(gòu)原理與用法詳解
這篇文章主要介紹了Java分支結(jié)構(gòu)和循環(huán)結(jié)構(gòu)原理與用法,結(jié)合實例形式分析了java分支結(jié)構(gòu)、循環(huán)結(jié)構(gòu)、跳轉(zhuǎn)語句等相關(guān)概念、原理、使用技巧與操作注意事項,需要的朋友可以參考下2020-02-02
SpringCloud Eureka自我保護(hù)機(jī)制原理解析
這篇文章主要介紹了SpringCloud Eureka自我保護(hù)機(jī)制原理解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-02-02

