一、Quartz 的核心概念
Quartz [kwɔːts] 是 OpenSymphony 开源组织在 Job scheduling 领域又一个开源项目,完全由 Java 开发,可以用来执行定时任务,类似于 java.util.Timer。但是相较于 Timer, Quartz 增加了很多功能:
1.持久性作业 - 就是保持调度定时的状态;
2.作业管理 - 对调度作业进行有效的管理;
Quartz 是一个强大任务调度框架,可以用来干嘛?如一个 OA 系统需要在每周五 9 点自动生成数据报表,或者想每月 10 号自动还款,又或者每周给暗恋的女生定时发送邮件,又或者人事系统会在每天早晨 8 点给有待办的人员自动发送 Email 提醒。
下面介绍 Quartz 的三个核心概念(job,Trigger,Scheduler)。
1、任务 job
job 就是你想实现的任务类,每一个 job 必须实现 org.quartz.job 接口,且只需实现接口定义的 execute() 方法。
Job:工作任务调度的接口,任务类需要实现该接口,该接口中定义 execute 方法,类似 jdk 提供的 TimeTask 类的 run 方法,在里面编写任务执行的业务逻辑。
Job: 实例在 Quartz 中的生命周期,每次调度器执行 job 时它在调用 execute 方法前,会创建一个新的 job 实例,当调用完成后,关联的 job 对象实例会被是释放,释放的实例会被垃圾回收机制回收。
2、触发器 Trigger
Trigger 为你执行任务的触发器,比如你想每天定时 1 点发送邮件,Trigger 将会设置 1 点执行该任务。
Trigger 主要包含两种:SimpleTrigger 和 CronTriggerr。
3、调度器 Scheduler
Scheduler 是任务的调度器,会将任务 job 和触发器 TRigger 结合,负责基于 Trigger 设定的时间执行 job。
二、Quartz 的几个常用 API
Scheduler :用于与调度程序交互的主程序接口。
Job : 预先定义的希望在未来时间被调度程序执行的任务类,自定义。
JobDetall :使用 JobDetail 来定义定时任务的实例,JobDetail 实例是通过 JobBuilder 类创建。
JobDataMap :可包含数据对象,在 job 实例执行的是好,可使用包含的数据;JobDataMap 是 java Map 接口的实现,增加了一些存取基本类型方法。
Trgger 触发器 :Trigger 对象是用于触发执行 Job 的,当调度一个 Job 时,我们实例一个触发器然后调整它的属性来满足 Job 执行的条件,表明任务在什么时候执行。定义了一个已经被安排的任务将在什么时候执行的时间条件,比如每秒执行一次。
JobBuilder :用于声明一个任务实例,也可以定义关于该任务的详情比如:任务名,组名等,这个声明的实例将作为一个实例执行的任务。
TriggerBuilder :触发器创建器,用于创建触发器 trigger 实例。
JobListener,TriggerListener,SchedulerListener 监听器,用于对组件的监听。
三、Spring Boot 集成 Quartz
1、创建项目并加入依赖(Spring Boot 的扩展默认集成 Quartz)
2、新建 Job 任务
3、创建 Schedule,执行任务
运行程序,控制台会每两秒打印一个 “Hello Quartz”。
三、Quartz 核心详解(部分)
1、Job 和 JobDetail
Job 是 Quartz 中的一个接口,接口下只有 execute 方法,在这个方法中编写业务逻辑。
JobDetail 用来绑定 Job,为 Job 实例提供许多属性:
name group jobClass jobDataMap
JobDetail 绑定指定的 Job,每次 Scheduler 调度执行一个 Job 的时候,首先会拿到对应的 Job,然后创建该 Job 实例,再去执行 Job 中的 execute() 的内容,任务执行结束后,关联的 Job 对象实例会被释放,且会被 JVM GC 清除。
为什么设计成 JobDetail + Job,不直接使用 Job
JobDetail 定义的是任务数据,而真正的执行逻辑是在 Job 中。 这是因为任务是有可能并发执行,如果 Scheduler 直接使用 Job,就会存在对同一个 Job 实例并发访问的问题。而 JobDetail & Job 方式,Sheduler 每次执行,都会根据 JobDetail 创建一个新的 Job 实例,这样就可以规避并发访问的问题。
2、Trigger、SimpleTrigger、CronTrigger
Trigger
Trigger 是 Quartz 的触发器,会去通知 Scheduler 何时去执行对应 Job。
new Trigger().startAt(): 表示触发器首次被触发的时间;
new Trigger().endAt(): 表示触发器结束触发的时间;
SimpleTrigger
SimpleTrigger 可以实现在一个指定时间段内执行一次作业任务或一个时间段内多次执行作业任务。
下面的程序就实现了程序运行 5s 后开始执行 Job,执行 Job 5s 后结束执行:
Date startDate = new Date();
startDate.setTime(startDate.getTime() + 5000);
Date endDate = new Date();
endDate.setTime(startDate.getTime() + 5000);
Trigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger1", "triggerGroup1")
.usingJobData("trigger1", "这是jobDetail1的trigger")
.startNow()//立即生效
.startAt(startDate)
.endAt(endDate)
.withSchedule(SimpleScheduleBuilder.simpleSchedule()
.withIntervalInSeconds(1)//每隔1s执行一次
.repeatForever())
.build();//一直执行
CronTrigger
CronTrigger 功能非常强大,是基于日历的作业调度,而 SimpleTrigger 是精准指定间隔,所以相比 SimpleTrigger,CroTrigger 更加常用。CroTrigger 是基于 Cron 表达式的。
public class MyScheduler2 {
public static void main(String[] args) throws SchedulerException, InterruptedException {
//1.创建JobDetail实例,并与PrintWordsJob类绑定(Job执行内容)
JobDetail jobDetail=JobBuilder.newJob(PrintWordsJob.class)
.usingJobData("jobDetail", "这个Job用来测试的")
.withIdentity("job","group")
.build();
//2.触发器
CronTrigger trigger=TriggerBuilder.newTrigger().withIdentity("trigger", "group").startNow()//立刻执行
.usingJobData("trigger1", "这是jobDetail1的trigger")
.withSchedule(CronScheduleBuilder.cronSchedule("0 * * * * ?"))//表示每次0秒时候执行。
.build();
//3.调度器
Scheduler scheduler = new StdSchedulerFactory().getScheduler();
scheduler.scheduleJob(jobDetail,trigger);
System.out.println("--------scheduler start ! ------------");
scheduler.start();
System.out.println("--------scheduler shutdown ! ------------");
}
}
3、JobDetail & Job 和 JobDataMap
JobDetail 是任务的定义,而 Job 是任务的执行逻辑。在 JobDetail 里会引用一个 Job Class 定义。
每一个 JobDetail 都会有一个 JobDataMap。JobDataMap 本质就是一个 Map 的扩展类,只是提供了一些更便捷的方法,比如 getString() 之类的。
1. 编写触发类
public static void demo2()throws Exception{
//1. 创建一个JobDetail,把实现了Job接口的类邦定到JobDetail
JobDetail jobDetail= JobBuilder.newJob(SecondJob.class)
.withIdentity("demo2")
.usingJobData("name","zhangan")
.usingJobData("age",22)
.build();
CronTrigger trigger=TriggerBuilder.newTrigger()
.withIdentity("trriger2")
.withSchedule(CronScheduleBuilder
.cronSchedule("45 50 10 * * ?"))
.build();
//创建schedule实例
StdSchedulerFactory factory = new StdSchedulerFactory();
//获取调度器实例
Scheduler scheduler = factory.getScheduler();
//开启调度器
scheduler.start();
//把SimpleTrigger和JobDetail注册给调度器
scheduler.scheduleJob(jobDetail,trigger);
}
2. 编写具体任务类
public class SecondJob implements Job {
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
//打印当前的执行时间 例如 2021-08-30 10:12:00
Date date = new Date();
//格式化时间
SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println("现在的时间是:"+ sf.format(date));
//具体的业务逻辑
System.out.println("开始生成任务报表 或 开始发送邮件");
JobDataMap jobDataMap = jobExecutionContext.getJobDetail().getJobDataMap();
String name= jobDataMap.getString("name");
int age= jobDataMap.getInt("age");
System.out.println("name="+name+",age="+age);
}
}