quartz+springboot注解_SpringBoot集成Quartz实现定时任务
1 需求
在我的前后端分离的实验室管理项⽬中,有⼀个功能是学⽣状态统计。我的设计是按天统计每种状态的⽐例。为了便于计算,在每天0点,
系统需要将学⽣的状态重置,并插⼊⼀条数据作为⼀天的开始状态。另外,考虑到学⽣的请假需求,请假的申请往往是提前做好,等系统时
间⾛到实际请假时间的时候,系统要将学⽣的状态修改为请假。
显然,这两个⼦需求都可以通过定时任务实现。在⽹上略做搜索以后,我选择了⽐较流⾏的定时任务框架Quartz。
2 Quartz
Quartz是⼀个定时任务框架,其他介绍⽹上也很详尽。这⾥要介绍⼀下Quartz⾥的⼏个⾮常核⼼的接⼝。
2.1 Scheduler接⼝
Scheduler翻译成调度器,Quartz通过调度器来注册、暂停、删除Trigger和JobDetail。Scheduler还拥有⼀个SchedulerContext,顾名
思义就是上下⽂,通过SchedulerContext我们可以获取到触发器和任务的⼀些信息。
2.2 Trigger接⼝
Trigger可以翻译成触发器,通过cron表达式或是SimpleScheduleBuilder等类,指定任务执⾏的周期。系统时间⾛到触发器指定的时间的
时候,触发器就会触发任务的执⾏。
2.3 JobDetail接⼝
Job接⼝是真正需要执⾏的任务。JobDetail接⼝相当于将Job接⼝包装了⼀下,Trigger和Scheduler实际⽤到的都是JobDetail。
3 SpringBoot官⽅⽂档解读
SpringBoot官⽅写了spring-boot-starter-quartz。使⽤过SpringBoot的同学都知道这是⼀个官⽅提供的启动器,有了这个启动器,集成的操
作就会被⼤⼤简化。
现在我们来看⼀看SpingBoot2.2.6官⽅⽂档,其中第4.20⼩节Quartz Scheduler就谈到了Quartz,但很可惜⼀共只有两页不到的内容,先
来看看这么精华的⽂档⾥能学到些什么。
Spring Boot offers several conveniences for working with the Quartz scheduler, including the
spring-boot-starter-quartz “Starter”. If Quartz is available, a Scheduler is auto-configured (through the SchedulerFactoryBean abstraction).
Beans of the following types are automatically picked up and associated with the Scheduler:
• JobDetail: defines a particular Job. JobDetail instances can be built with the JobBuilder API.
• Calendar.
• Trigger: defines when a particular job is triggered.
翻译⼀下:
SpringBoot提供了⼀些便捷的⽅法来和Quartz协同⼯作,这些⽅法⾥⾯包括`spring-boot-starter-quartz`这个启动器。如果Quartz可⽤,Scheduler会通过SchedulerFa JobDetail、Calendar、Trigger这些类型的bean会被⾃动采集并关联到Scheduler上。
Jobs can define setters to inject data map properties. Regular beans can also be injected in a similar manner.
翻译⼀下:
Job可以定义setter(也就是set⽅法)来注⼊配置信息。也可以⽤同样的⽅法注⼊普通的bean。
下⾯是⽂档⾥给的⽰例代码,我直接完全照着写,拿到的却是null。不知道是不是我的使⽤⽅式有误。后来仔细⼀想,⽂档的意思应该是在创建Job对象之后,调⽤set⽅法将依赖注⼊进去。但后⾯我们是通过框架反射⽣成的Job对象,这样做反⽽会搞得更加复杂。最后还是决定采⽤给Job类加@Component注解的⽅法。
⽂档的其他篇幅就介绍了⼀些配置,但是介绍得也不全⾯,看了帮助也并不是很⼤。详细的配置可以参考w3school的Quartz配置。
4 SpringBoot集成Quartz
4.1 建表
我选择将定时任务的信息保存在数据库中,优点是显⽽易见的,定时任务不会因为系统的崩溃⽽丢失。
建表的sql语句在Quartz的github中可以到,⾥⾯有针对每⼀种常⽤数据库的sql语句,具体地址是:Quartz数据库建表sql。
建表以后,可以看到数据库⾥多了11张表。我们完全不需要关⼼每张表的具体作⽤,在添加删除任务、触发器等的时候,Quartz框架会操作这些表。
4.2 引⼊依赖
在l⾥添加依赖。
<!-- quartz 定时任务 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
<version>2.2.6.RELEASE</version>
</dependency>
4.3 配置quartz
在l中配置quartz。相关配置的作⽤已经写在注解上。
# spring的datasource等配置未贴出
spring:
quartz:
# 将任务等保存化到数据库
job-store-type: jdbc
# 程序结束时会等待quartz相关的内容结束
wait-for-jobs-to-complete-on-shutdown: true
# QuartzScheduler启动时更新⼰存在的Job,这样就不⽤每次修改targetObject后删除qrtz_job_details表对应记录
overwrite-existing-jobs: true
# 这⾥居然是个map,搞得智能提⽰都没有,佛了
properties:
org:
quartz:
# scheduler相关
scheduler:
# scheduler的实例名
instanceName: scheduler
instanceId: AUTO
# 持久化相关
jobStore:
class: org.quartz.impl.jdbcjobstore.JobStoreTX
driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate
# 表⽰数据库中相关表是QRTZ_开头的
tablePrefix: QRTZ_
useProperties: false
# 线程池相关
threadPool:
class: org.quartz.simpl.SimpleThreadPool
# 线程数
threadCount: 10
# 线程优先级
threadPriority: 5
threadsInheritContextClassLoaderOfInitializingThread: true
4.4 注册周期性的定时任务
第1节中提到的第⼀个⼦需求是在每天0点执⾏的,是⼀个周期性的任务,任务内容也是确定的,所以直接在代码⾥注册JobDetail和Trigger的bean就可以了。当然,这些JobDetail和Trigger也是会被持久化到数据库⾥。
/**
* Quartz的相关配置,注册JobDetail和Trigger
* 注意JobDetail和Trigger是org.quartz包下的,不是spring包下的,不要导⼊错误
*/
@Configuration
public class QuartzConfig {
@Bean
public JobDetail jobDetail() {
JobDetail jobDetail = wJob(StartOfDayJob.class)
.withIdentity("start_of_day", "start_of_day")
.storeDurably()
.
build();
return jobDetail;
}
@Bean
public Trigger trigger() {
Trigger trigger = wTrigger()
.forJob(jobDetail())
.withIdentity("start_of_day", "start_of_day")
.startNow()
// 每天0点执⾏
.Schedule("0 0 0 * * ?"))
.
build();
return trigger;
}
}
builder类创建了⼀个JobDetail和⼀个Trigger并注册成为Spring bean。从第3节中摘录的官⽅⽂档中,我们已经知道这些bean会⾃动关联到调度器上。需要注意的是JobDetail和Trigger需要设置组名和⾃⼰的名字,⽤来作为唯⼀标识。当然,JobDetail和Trigger的唯⼀标识可以相同,因为他们是不同的类。
Trigger通过cron表达式指定了任务执⾏的周期。对cron表达式不熟悉的同学可以百度学习⼀下。
JobDetail⾥有⼀个StartOfDayJob类,这个类就是Job接⼝的⼀个实现类,⾥⾯定义了任务的具体内容,看⼀下代码:
@Component
public class StartOfDayJob extends QuartzJobBean {
private StudentService studentService;
@Autowired
public StartOfDayJob(StudentService studentService) {
this.studentService = studentService;
}
@Override
protected void executeInternal(JobExecutionContext jobExecutionContext)
throws JobExecutionException {
// 任务的具体逻辑
}
}
这⾥⾯有⼀个⼩问题,上⾯⽤builder创建JobDetail时,传⼊了StartOfDayJob.class,按常理推测,应该是Quartz框架通过反射创建StartOfDayJob对象,再调⽤executeInternal()执⾏任务。这样依赖,这个Job是Quartz通过反射创建的,即使加了注解
@Component,这个StartOfDayJob对象也不会被注册到ioc容器中,更不可能实现依赖的⾃动装配。
⽹上很多博客也是这么介绍的。但是根据我的实际测试,这样写可以完成依赖注⼊,但我还不知道它的实现原理。
4.5 注册⽆周期性的定时任务
第1节中提到的第⼆个⼦需求是学⽣请假,显然请假是不定时的,⼀次性的,⽽且不具有周期性。
4.5节与4.4节⼤体相同,但是有两点区别:
Job类需要获取到⼀些数据⽤于任务的执⾏;
任务执⾏完成后删除Job和Trigger。
业务逻辑是在⽼师批准学⽣的请假申请时,向调度器添加Trigger和JobDetail。
实体类:
public class LeaveApplication {
@TableId(type = IdType.AUTO)
private Integer id;
private Long proposerUsername;
@JsonFormat( pattern = "yyyy-MM-dd HH:mm",timezone="GMT+8")
private LocalDateTime startTime;
@JsonFormat( pattern = "yyyy-MM-dd HH:mm",timezone="GMT+8")
private LocalDateTime endTime;
spring ioc注解
private String reason;
private String state;
private String disapprovedReason;
private Long checkerUsername;
private LocalDateTime checkTime;
// 省略getter、setter
}

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。