SpringBoot快速整合Quartz动态管理定时任务
背景
如题,上个版本产品提出了一个报告需求的整体大功能,其中有一个小的子功能就是开发一个在页面==动态添加、删除、修改、启停==的定时任务来执行一系列的操作(生成周期报告、发送邮件等);关于SpringBoot中的定时任务,大家应该都比较熟悉,在主启动类上添加***@EnableScheduling***,然后直接在需要定时执行的方法上直接添加***@Scheduled(cron = “”)并且添加上定时任务的cron表达式即可;但是这种方式有一个局限性就是,在编码阶段就已经将任务的个数,执行周期全部的确定好了,没办法在系统运行时进行***动态的增删改查,所以这里不就符合我们的需求,这时***Quartz***就该上场了。
***注:***我这里又没有使用到整个Quartz的功能,我只是使用了它的任务调度及触发功能;关于任务的存储和持久化,我直接使用了数据库创建一个自己的表来存储;这样有一个好处,任务的信息可以直接拿给前端去使用,免去了复杂的转换数据,并且在后期如果有拓展信息的需求也可以更方便和快捷的添加;这样做的需要在项目启动时,将数据库已经保存的数据根据需要依次加载到内存中,这样就可以不用创建Quartz大量的数据库表,并且, 我这边的项目已经有其他的模块在使用那些任务和表了,我想区分开,让各个不同模块的定时任务都分开,独立出来。
**附:**开发后页面的功能如下:
什么是quartz
Quartz是一个完全由java编写的开源作业调度框架。不要让作业调度这个术语吓着你。尽管Quartz框架整合了许多额外功能, 但就其简易形式看,你会发现它易用得简直让人受不了! ------ 来自百度词条
其实关于Quartz现在在网上已经有很多的文章和帖子介绍了,大家也可以自己去了解了解,这里的重点是SpringBoot项目中,怎么快速的整合它;好消息是,现在的SpringBoot中已经继承了Quartz的starer了,所以大伙可以更方便的使用它了,甚至使用的它的简单功能时,都不需要不用自己主动去注入了,直接@Autowire 一键注入!太香了!
相关概念
在了解Quartz时,有几个比较重要的名词和概念需要了解一下,这几个概念了解透彻了,整合起来会更加的顺畅、丝滑!他们的关系如下
-
Scheduler:任务调度器,用来管理和调度任务;
-
Trigger:触发器,存放Job执行的时间策略,用于定义任务调度时间规则。
Trigger触发器有:SimpleTrigger、CalendarIntervalTrigger、DailyTimeIntervalTrigger、CronTrigger; 其中常用的有两种***SimpleTrigger*** 和 CronTrigger
- SimpleTrigger:指定从某一个时间开始,以一定的时间间隔(单位是毫秒)执行的任务。
- 它适合的任务类似于:9:00 开始,每隔1小时,执行一次。
- CronTrigger:适合于更复杂的任务,它支持类型于Linux Cron的语法(并且更强大)。基本上它覆盖了其他Trigger的绝大部分能力(但不是全部)—— 当然,也更难理解。 所以 CronTrigger比SimpleTrigger更常用,当你需要一个基于日历般概念的作业调度器,而不是像SimpleTrigger那样精确指定间隔时间。所以我们选择了CronTrigger。
- SimpleTrigger:指定从某一个时间开始,以一定的时间间隔(单位是毫秒)执行的任务。
-
Job &obDetail:JobDetail是任务的定义,而Job是任务的执行逻辑。在JobDetail里会引用一个Job Class定义,可以理解为JobDetail是Job的进一步信息的封装。
-
Cron: Cron表达式相信大家都比较熟悉了,这里的Cron写法基本和Linux 中的Crontab表达式一致,但是有一些细节还是有区别的,建议大家生成时,去网站进行校验一下。
本文主要讲解
-
SpringBoot快速整合Quartz(主要是快速、简单)
-
动态任务的基于内存,但是项目重启了定时任务信息就没了,所以需要将任务信息持久化到数据库
-
动态添加的任务数据的持久化是自己创建的数据库表,而不是Quartz框架中的表
步骤
1. 引入POM依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
<version>2.2.10.RELEASE</version>
</dependency>
2. 编写job实现类
// 这里我直接实现了QuartzJobBean
@Component
@Slf4j
public class ReportJob extends QuartzJobBean {
@Override
protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
try {
// 需要执行任务详细业务代码 。。。
// 此处catch一下,为了在执行失败后,不让quartz再执行一次业务逻辑,导致业务数据错误,
// 正常应该是不应该将异常抛到此处的,但是由于我的项目需要,只能在这里进行捕捉了,这里是由于Quartz的失败重试机制,容易导致数据异常。
} catch (Exception e) {
log.error("{}:执行失败,跳过此次任务执行。",jobExecutionContext.getJobDetail().getKey().getName());
}
}
}
3. 业务数据PO代码
由于我没有使用Quartz JobStores中的数据库表,这里就是我对前端页面传过来的数据进行保存进自定义数据库表对应的PO。
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName(value = "t_report_subscribe", autoResultMap = true)
@NoArgsConstructor
public class ReportSubscribePO implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 主键
*/
@ApiModelProperty(value = "主键id")
@TableId(value = "id")
@JsonFormat(shape = JsonFormat.Shape.STRING)
private Long id;
/**
* 报告名称
*/
@ApiModelProperty(value = "报告名