Quartz框架
轻量级的任务调度框架,支持作业调度、持久化、任务持久化、并发控制、失败重试等。与spring自带定时任务最主要的区别是他支持定时年月日时分秒存储
核心组件
调度器:Scheduler 任务 :Job 触发器:Trigger,包括SimpleTrigger和CronTrigger
- Job(任务):是一个接口,有一个方法
void execute(JobExecutionContext context),可以通过实现该接口来定义需要执行的任务(具体的逻辑代码)。
- JobDetail:Quartz每次执行Job时,都重新创建一个Job实例,会接收一个Job实现类,以便运行的时候通过
newInstance()的反射调用机制去实例化Job。JobDetail是用来描述Job实现类以及相关静态信息,比如任务在scheduler中的组名等信息。
- Trigger(触发器):描述触发Job执行的时间触发规则实现类SimpleTrigger和CronTrigger可以通过crom表达式定义出各种复杂的调度方案。
- Calendar:是一些日历特定时间的集合。一个Trigger可以和多个 calendar关联,比如每周一早上10:00执行任务,法定假日不执行,则可以通过calendar进行定点排除。
- Scheduler(调度器):代表一个调度容器,一个调度容器中可以注册多个 JobDetail 和 Trigger。Scheduler可以将Trigger绑定到某一JobDetail上,这样当Trigger被触发时,对应的Job就会执行。一个Job可以对应多个Trigger,但一个Trigger只能对应一个Job。
Scheduler 调度器 -> Trigger 触发器-> JobDetail 工作策略 ->Job 工作
Quartz框架的工作流程
Quartz框架的工作流程如下:
- 首先,定义需要执行的任务(Job),并指定任务的执行规则(Trigger)。
- 其次,将任务和触发器注册到调度器中。
- 最后,调度器按照指定的条件和规则执行任务。
使用
-
导入依赖
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-quartz --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-quartz</artifactId> </dependency> -
实现定时业务逻辑
创建一个业务类,继承 job 接口,并实现 execute() 方法,方法中即可实现相应业务逻辑
import org.quartz.Job; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; public class AlphaJob implements Job { @Override public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException { // 业务逻辑代码略 } } -
编写触发器类
定义任务的调度规则,实现Trigger接口。
public class HelloScheduler { public static void main(String[] args) throws SchedulerException { //1.创建一个jobDetail的实例,将该实例与HelloJob Class绑定 JobDetail jobDetail = JobBuilder .newJob(HelloJob.class) //定义Job类为HelloJob类,真正的执行逻辑所在 .withIdentity("myJob", "group1") //定义name 和 group .usingJobData("message","hello myJob1") //加入属性到jobDataMap .usingJobData("FloatJobValue",8.88f) //加入属性到jobDataMap .build(); //2.创建一个Trigger触发器的实例,定义该job立即执行,并且每2秒执行一次,一直执行 SimpleTrigger trigger = TriggerBuilder.newTrigger() .withIdentity("myTrigger", "group1") .startNow() .withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(2).repeatForever()) .build(); //3.创建schedule实例 StdSchedulerFactory factory = new StdSchedulerFactory(); Scheduler scheduler = factory.getScheduler(); scheduler.start(); //启动 scheduler.scheduleJob(jobDetail,trigger); // jobDetail和trigger加入调度 } }
案例:
需求:假设你正在开发一个在线学习平台,需要实现以下定时任务:
每天凌晨1点,统计当天新增用户数量,并发送邮件通知管理员。 每周一凌晨2点,计算上一周的学习活跃用户,将活跃用户信息保存到数据库。
- 实现job逻辑
@Component
public class ActiveUserStatsJob implements Job {
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
// 在这里编写计算活跃用户信息的逻辑
System.out.println("计算学习活跃用户并保存到数据库...");
}
}
@Component
public class NewUserStatsJob implements Job {
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
// 在这里编写统计新增用户的逻辑
System.out.println("统计新增用户数量并发送邮件通知管理员...");
}
}
- 配置触发器和调度器
/**
* quartz配置类
* 注意: 添加后运行程序即可quartz自动运行
* @version 1.0.0
* @date 2023/8/3
*/
@Configuration
public class QuartzConfig {
@Bean
public JobDetail newUserStatsJobDetail() {
return newJob(NewUserStatsJob.class)
.withIdentity("newUserStatsJob")
.storeDurably()
.build();
}
@Bean
public Trigger newUserStatsJobTrigger() {
return newTrigger()
.forJob(newUserStatsJobDetail())
.withIdentity("newUserStatsJobTrigger")
// .withSchedule(cronSchedule("0 0 1 * * ?")) // 每天凌晨1点执行
.withSchedule(cronSchedule("0 0/3 * * * ?")) // 3分钟
.build();
}
@Bean
public JobDetail activeUserStatsJobDetail() {
return newJob(ActiveUserStatsJob.class)
.withIdentity("activeUserStatsJob")
.storeDurably()
.build();
}
@Bean
public Trigger activeUserStatsJobTrigger() {
return newTrigger()
.forJob(activeUserStatsJobDetail())
.withIdentity("activeUserStatsJobTrigger")
// .withSchedule(cronSchedule("0 0 2 * * MON")) // 每周一凌晨2点执行
.withSchedule(cronSchedule("0 0/2 * * * ?")) // 两分钟执行
.build();
}
}
常见时间(corn语法)
"30 * * * * ?" 每半分钟触发任务
"30 10 * * * ?" 每小时的10分30秒触发任务
"30 10 1 * * ?" 每天1点10分30秒触发任务
"30 10 1 20 * ?" 每月20号1点10分30秒触发任务
"30 10 1 20 10 ? *" 每年10月20号1点10分30秒触发任务
"30 10 1 20 10 ? 2011" 2011年10月20号1点10分30秒触发任务
"30 10 1 ? 10 * 2011" 2011年10月每天1点10分30秒触发任务
"30 10 1 ? 10 SUN 2011" 2011年10月每周日1点10分30秒触发任务
"15,30,45 * * * * ?" 每15秒,30秒,45秒时触发任务
相比spring自带的定时触发器的好处
spring自带的定时触发器,如springtask
Spring task 优点:无需整合spring,作业类中就可以调用业务service 缺点:单线程;不能做数据存储型的定时任务,不能定时到年月日时分秒 Quartz 优点:多线程;可以做数据存储型的定时任务,维护性高; 缺点:需要整合spring,不能直接调用业务层service;