SpringBoot集成Quartz完成守时使命

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会经过SchedulerFactoryBean这个工厂bean主动装备到SpringBoot里。
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 引进依靠
在pom.xml里增加依靠。

<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
<version>2.2.6.RELEASE</version>

4.3 装备quartz
在application.yml中装备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 = JobBuilder.newJob(StartOfDayJob.class)
.withIdentity("start_of_day", "start_of_day")
.storeDurably()
.build();
return jobDetail;
}
@Bean
public Trigger trigger() {
Trigger trigger = TriggerBuilder.newTrigger()
.forJob(jobDetail())
.withIdentity("start_of_day", "start_of_day")
.startNow()
// 每天0点履行
.withSchedule(CronScheduleBuilder.cronSchedule("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;
private String reason;
private String state;
private String disapprovedReason;
private Long checkerUsername;
private LocalDateTime checkTime;
// 省掉getter、setter

}

Service层逻辑,重要的当地已在注释中阐明。

@Service
public class LeaveApplicationServiceImpl implements LeaveApplicationService {

@Autowired
private Scheduler scheduler;
// 省掉其他办法与其他依靠
/**
* 增加job和trigger到scheduler
*/
private void addJobAndTrigger(LeaveApplication leaveApplication) {
Long proposerUsername = leaveApplication.getProposerUsername();
// 创立请假开端Job
LocalDateTime startTime = leaveApplication.getStartTime();
JobDetail startJobDetail = JobBuilder.newJob(LeaveStartJob.class)
// 指定使命组名和使命名
.withIdentity(leaveApplication.getStartTime().toString(),
proposerUsername + "_start")
// 增加一些参数,履行的时分用
.usingJobData("username", proposerUsername)
.usingJobData("time", startTime.toString())
.build();
// 创立请假开端使命的触发器
// 创立cron表达式指定使命履行的时刻,因为请假时刻是确认的,所以年月日时分秒都是确认的,这也契合使命只履行一次的要求。
String startCron = String.format("%d %d %d %d %d ? %d",
startTime.getSecond(),
startTime.getMinute(),
startTime.getHour(),
startTime.getDayOfMonth(),
startTime.getMonth().getValue(),
startTime.getYear());
CronTrigger startCronTrigger = TriggerBuilder.newTrigger()
// 指定触发器组名和触发器名
.withIdentity(leaveApplication.getStartTime().toString(),
proposerUsername + "_start")
.withSchedule(CronScheduleBuilder.cronSchedule(startCron))
.build();
// 将job和trigger增加到scheduler里
try {
scheduler.scheduleJob(startJobDetail, startCronTrigger);
} catch (SchedulerException e) {
e.printStackTrace();
throw new CustomizedException("增加请假使命失利");
}
}

}
Job类逻辑,重要的当地已在注释中阐明。

@Component
public class LeaveStartJob extends QuartzJobBean {

private Scheduler scheduler;
private SystemUserMapperPlus systemUserMapperPlus;
@Autowired
public LeaveStartJob(Scheduler scheduler,
SystemUserMapperPlus systemUserMapperPlus) {
this.scheduler = scheduler;
this.systemUserMapperPlus = systemUserMapperPlus;
}
@Override
protected void executeInternal(JobExecutionContext jobExecutionContext)
throws JobExecutionException {
Trigger trigger = jobExecutionContext.getTrigger();
JobDetail jobDetail = jobExecutionContext.getJobDetail();
JobDataMap jobDataMap = jobDetail.getJobDataMap();
// 将增加使命的时分存进去的数据拿出来
long username = jobDataMap.getLongValue("username");
LocalDateTime time = LocalDateTime.parse(jobDataMap.getString("time"));
// 编写使命的逻辑
// 履行之后删去使命
try {
// 暂停触发器的计时
scheduler.pauseTrigger(trigger.getKey());
// 移除触发器中的使命
scheduler.unscheduleJob(trigger.getKey());
// 删去使命
scheduler.deleteJob(jobDetail.getKey());
} catch (SchedulerException e) {
e.printStackTrace();
}
}

}

5 总结
上文所述的内容应该能够满意绝大部分守时使命的需求。我在查阅网上的博客之后,发现大部分博客里介绍的Quartz运用仍是停留在Spring阶段,装备也都是经过xml,因而我在完结了功用今后,将整个进程总结了一下,留给需求的人以及今后的自己做参阅。

总体上来说,Quartz完结守时使命仍是十分便利的,与SpringBoot整合之后装备也十分简略,是完结守时使命的不错的挑选。

5.2 小坑1
在IDEA2020.1版本里运用SpringBoot与Quartz时,报错找不到org.quartz程序包,可是依靠里边分明有org.quartz,类里的import也没有报错,还能够经过Ctrl+鼠标左键直接跳转到相应的类里。后边我用了IDEA2019.3.4就不再有这个过错。那么便是新版IDEA的BUG了。

本文由博客群发一文多发等运营东西渠道 OpenWrite 发布

原文地址https://www.cnblogs.com/FatShallot/p/12834352.html