Quartz是一个由java编写的开源的Job调度框架,下面对其进行一个比较详细的介绍
- 核心组件API介绍
- 调度流程分析
- 调度示例
核心组件API介绍
SchedulerFactory / Scheduler / QuartzScheduler /
- SchedulerFactory是用来获取调度者scheduler的一个工厂类(scheduler并没有提供构造方法),API如下
Collection<Scheduler> getAllSchedulers()
、Scheduler getScheduler()
、Scheduler getScheduler(String schedName)
- Scheduler是Quartz中的核心接口,该类实例内部维护了一个JobDetails和Trigger的列表,主要执行调度工作,如添加、删除、获取作业列表、自动或手动触发作业等操作,下面是Scheduler接口中的部分API
void
addJob(JobDetail jobDetail, boolean replace)
job存在而replace为false的情况下抛出异常boolean
checkExists(JobKey jobKey)
boolean
checkExists(TriggerKey triggerKey)
void
clear()
boolean
deleteJob(JobKey jobKey)
SchedulerContext
getContext()
Returns theSchedulerContext
of theScheduler
.List<JobExecutionContext>
getCurrentlyExecutingJobs()
返回此调度程序实例中当前正在执行的所有作业。JobDetail
getJobDetail(JobKey jobKey)
List<String>
getJobGroupNames()
Set<JobKey>
getJobKeys(GroupMatcher<JobKey> matcher)
ListenerManager
getListenerManager()
返回scheduler调度器的监听管理器Trigger
getTrigger(TriggerKey triggerKey)
List<String>
getTriggerGroupNames()
Set<TriggerKey>
getTriggerKeys(GroupMatcher<TriggerKey> matcher)
List<? extends Trigger>
getTriggersOfJob(JobKey jobKey)
Trigger.TriggerState
getTriggerState(TriggerKey triggerKey)
boolean
interrupt(JobKey jobKey)
中断,该操作会中断当前运行中的,且实现了InterruptableJob接口的所有作业,依次调用每个作业的interrupt(),但如果某个作业中断时抛出异常(实际开发中,作业中的中断异常是否抛出由开发者决定),后续作业的interrupt()方法就不会再被调用,
boolean
interrupt(String fireInstanceId)
中断单个工作任务void
pauseJob(JobKey jobKey)
暂停给定Job,及关联的所有触发器。(每个触发器只会对应一个Job)void
pauseJobs(GroupMatcher<JobKey> matcher)
暂停组中的所有JobDetails,及它们的所有触发器void
pauseTrigger(TriggerKey triggerKey)
暂停触发器void
pauseTriggers(GroupMatcher<TriggerKey> matcher)
暂停组中的所有触发器Date
rescheduleJob(TriggerKey triggerKey, Trigger newTrigger)
删除原触发器,并添加新触发器,绑定的任务不变。相当修改作业的触发器一样。void
resumeJob(JobKey jobKey)
恢复给定Jobvoid
resumeTrigger(TriggerKey triggerKey)
void
scheduleJob(JobDetail jobDetail, Set<? extends Trigger> triggersForJob, boolean replace)
执行Job,如果Job或triggers已经存在,通过replace来决定是否覆盖。Date
scheduleJob(JobDetail jobDetail, Trigger trigger)
Add the given
to the Scheduler, and associate the givenJobDetail
with it.Trigger
Date
scheduleJob(Trigger trigger)
执行该触发器绑定的任务void
scheduleJobs(Map<JobDetail,Set<? extends Trigger>> triggersAndJobs, boolean replace)
批量执行Jobvoid
setJobFactory(JobFactory factory)
设置将负责生成作业类实例的JobFactory,一般情况下通过实现Job接口即可,用不到JobFactory。void
shutdown()
关闭调度程序,会清理所有资源void
shutdown(boolean waitForJobsToComplete)
等待正处于执行中的Job执行完后,再关闭调用程序void
standby()
暂停调度程序(调用程序进行待命模式),与关闭不同,调度程序可以在任何时候重新启动。void
start()
启动触发器的调度程序线程void
startDelayed(int seconds)
延迟启动void
triggerJob(JobKey jobKey)
立即执行给定Jobvoid
triggerJob(JobKey jobKey, JobDataMap data)
立即执行给定Job(有参)boolean
unscheduleJob(TriggerKey triggerKey)
删除指定的触发器,如果关联的作业没有任何其他触发器,且该作业不是持久的,则该作业也将被删除。boolean
unscheduleJobs(List<TriggerKey> triggerKeys)
- QuartzScheduler是Scheduler接口的间接实现,包含调度作业、注册JobListener实例等方法。它并不是Scheduler的子类,但是常用的StdScheduler就是对它的包装。
public class StdScheduler implements Scheduler { private QuartzScheduler sched; ...
JobBuilder / JobDetail / Job / InterruptableJob / JobKey / JobStore / JobDataMap
- JobBuilder用来构造JobDetail对象(jobDetails同样没有提供构造函数)
- JobDetail是对Job的一个装饰(功能更强),Quartz中不会直接存储Job类,而是用包装后的JobDetail代表一个作业实例
- Job接口才是程序业务真正执行的地方,开发中的任务类一般都实现该接口,一个Job可以理解成一个开发好的任务类。
void
execute(JobExecutionContext context)
- InterruptableJob(可中断的Job)是Job的子接口,提供了一个中断功能,实现该接口的作业可以被中断
void
interrupt()
在业务类中通过该方法,响应中断操作 - JobKey唯一的标识一个JobDetail,键由名称和组组成,相同组中的名称必须是唯一的。
- JobDataMap,是JobDetail对象的一部分,本质上是Java Map接口的一个实现,提供一系列的put/get等方法,常用来存储一些Job执行时需要的环境参数。
TriggerBuilder / Trigger / TriggerKey / SimpleTrigger / CronTrigger
TriggerBuilder 用来构造Trigger。一个触发器只能指向一个Job,多个触发器可以指向同一个Job(即一个Job可以被多个触发器触发)。
TriggerKey唯一的标识一个触发器,键同样由名称和组组成,组中的名称必须是唯一的。
SimpleTrigger是一种比较简单的Trigger,适用执行一次,或指定时间点执行,或在给定的延迟内重复N次这些场景;而CromTrigger功能强大很多,允许根据每天、每周、每月和每年的月的时间进行调度,支持cron表达式。
Trgger详解
Cron表达式详解
Cron-Expressions是由七个子表达式组成的一个字符串,每个子表达式之间用空格隔开,每个子表达式含义及常量值如下
字段名称 | 强制性 | 允许的值 | 允许的特殊字符 |
---|---|---|---|
秒 | 是 | 0-59 | , - * / |
分 | 是 | 0-59 | , - * / |
时 | 是 | 0-23 | , - * / |
日 | 是 | 1-31(准确的说是1-当月最后一天) | , - *?/ LW |
月 | 是 | 1-12或JAN-DEC(官网其它地方7说0-11,经测试是错误的说法) | , - * / |
星期几/周几 | 是 | 1-7或SUN-SAT(英文不区分大小写,上同) | , - *?/ L# |
年 | 没有(可为空) | 空,1970-2099 | , - * / |
关于特殊字符的介绍
- “ * "表示“每”的意思,如月表达式"*"表示每个月,周表达式"*"表示一周中的每一天。
- " / "表示增量,如分表达式"0/15",则表示“从第0分钟开始,每隔15分钟...",相当于分表达式"0,15,30,45"。
但单纯的"/15"则表示每小时的第15分钟,并不表示每15分钟...,等价于"0,15" - " ? "只能在日和周表达式中使用,表示“无特定值”。
- “L”是“last”的简称,只允许在日和周表达式中使用。单独使用时,月表达式“?”表示月的最后一日,周表达式“?”表示周的最后一天(周六)。综合使用时,如“FRIL”或"6L"则表示“该月的最后一个星期五“;“L-3”表示每月的倒数第三天。
官方文档对于“L"选项的建议是:使用“L”选项时,不要指定列表或值范围,否则容易产生意外结果。 - "W" 只允许用于日表达式中,表示最接近给定日期的工作日(周一至周五)。如"15W"表示最接近15号的工作日,如"LW"则表示最后一个工作日。
- "#" 只允许用于周表达式中,用于指定该月的“第n个周n"。例如,周表达式“6#3”或“FRI#3”都表示“该月的第三个星期五”。
比如
"0 0 12 ? * WED" — 表示”每周三中午12:00:00“。
“0 0/5 * * *?” 表示从0分开始,每隔5分钟执行一次。
“10 0/5 * * *?” 表示从0分10秒开始,每隔5分钟执行一次。
“0 30 10-13?* WED,FRI“ 表示每周三和周五的10点到13点时(含),第30分钟时执行一次。即10:30,11:30,12:30,13:30。
“0 0/30 8-9 5,20 *?” 每月5号和20号,8点和9点时,从0分开始,每隔30分钟执行一次。即8:00、8:30、9:00、9:30
JobListener / TriggerListener / SchedulerListener
- JobListener接口监听JobDetail的执行并提供相应通知,如执行前、执行后等。
- TriggerListener接口提供Trigger触发情况,如触发器被触发、触发完成(即关联的JobDetail执行完成)、触发失败等。
- SchedulerListener 接口提供调度器事件通知,如scheduler的启动/停止、JobDetail的添加/移除/暂停/恢复运行、Trigger的暂停/恢复等事件。
注意点
- 任务类必须是public修饰,所以不能定义成内部类,也不能是同文件中非public类。否则异常: Problem instantiating class 'cn.net.quartz.demo.TestJob' [See nested exception: java.lang.IllegalAccessException: Class org.quartz.simpl.SimpleJobFactory can not access a member of class cn.net.quartz.demo.TestJob with modifiers "public"]
- 没有绑定触发器的JobDetail必须持久,否则添加异常(warn: Jobs added with no trigger must be durable. ),必须在添加之前给作业绑定触发器、或者把作业设置成持久的storeDurably()
- 任务参数JobDataMap,在持久化时也会持久化
- 如果Job类中提供了setXxx(xx)方法,而且构造JobDetail时JobDataMap存在key=xxx,那会在构造过程中将会注入到任务类本身的setXxx();
- context中的dataMap是合并之后的map,JobDataMap dataMap = context.getMergedJobDataMap(); 这是JobDetail.JobDataMap和Trigger.JobDataMap的合并,相同key的情况下后者覆盖前者。