一、quartz核心概念
先来看一张图:
scheduler |
任务调度器 |
trigger |
触发器,用于定义任务调度时间规则 |
job |
任务,即被调度的任务 |
misfire |
错过的,指本来应该被执行但实际没有被执行的任务调度 |
- Job:是一个接口,只有一个方法void execute(JobExecutionContext context),开发者实现该接口定义运行任务,JobExecutionContext类提供了调度上下文的各种信息。Job运行时的信息保存在JobDataMap实例中;
- JobDetail:Quartz在每次执行Job时,都重新创建一个Job实例,所以它不直接接受一个Job的实例,相反它接收一个Job实现类,以便运行时通过newInstance()的反射机制实例化Job。因此需要通过一个类来描述Job的实现类及其它相关的静态信息,如Job名字、描述、关联监听器等信息,JobDetail承担了这一角色。
- Trigger:是一个类,描述触发Job执行的时间触发规则。主要有SimpleTrigger和CronTrigger这两个子类。当仅需触发一次或者以固定时间间隔周期执行,SimpleTrigger是最适合的选择;而CronTrigger则可以通过Cron表达式定义出各种复杂时间规则的调度方案:如每早晨9:00执行,周一、周三、周五下午5:00执行等;
- Calendar:org.quartz.Calendar和java.util.Calendar不同,它是一些日历特定时间点的集合(可以简单地将org.quartz.Calendar看作java.util.Calendar的集合——java.util.Calendar代表一个日历时间点,无特殊说明后面的Calendar即指org.quartz.Calendar)。一个Trigger可以和多个Calendar关联,以便排除或包含某些时间点。假设,我们安排每周星期一早上10:00执行任务,但是如果碰到法定的节日,任务则不执行,这时就需要在Trigger触发机制的基础上使用Calendar进行定点排除。
- Scheduler:代表一个Quartz的独立运行容器,Trigger和JobDetail可以注册到Scheduler中,两者在Scheduler中拥有各自的组及名称,组及名称是Scheduler查找定位容器中某一对象的依据,Trigger的组及名称必须唯一,JobDetail的组和名称也必须唯一(但可以和Trigger的组和名称相同,因为它们是不同类型的)。Scheduler定义了多个接口方法,允许外部通过组及名称访问和控制容器中Trigger和JobDetail。
Scheduler可以将Trigger绑定到某一JobDetail中,这样当Trigger触发时,对应的Job就被执行。一个Job可以对应多个Trigger,但一个Trigger只能对应一个Job。可以通过SchedulerFactory创建一个Scheduler实例。Scheduler拥有一个SchedulerContext,它类似于ServletContext,保存着Scheduler上下文信息,Job和Trigger都可以访问SchedulerContext内的信息。SchedulerContext内部通过一个Map,以键值对的方式维护这些上下文数据,SchedulerContext为保存和获取数据提供了多个put()和getXxx()的方法。可以通过Scheduler# getContext()获取对应的SchedulerContext实例;
- ThreadPool:Scheduler使用一个线程池作为任务运行的基础设施,任务通过共享线程池中的线程提高运行效率。
位置 |
含义 |
允许的特殊字符 |
1 |
秒(0~59) |
, - * / |
2 |
分(0~59) |
, - * / |
3 |
小时(0~24) |
, - * / |
4 |
日期(1~31) |
, - * / ? L W C |
5 |
月(JAN~DEC或1~12) |
, - * / |
6 |
星期(SUN~SAT或1~7) |
, - * / ? L C # |
7 |
年(可选,1970~2099),若为空,表示全部时间范围 |
, - * / |
特殊字符 |
说明 |
* |
通配符,任意值 |
? |
无特定值。通常和其他指定的值一起使用,表示必须显示该值但不能检查 |
- |
范围。e.g.小时部分10-12表示10:00,11:00, 12:00 |
, |
列分隔符。可以让你指定一系列的值。e.g.在星期域中指定MON、TUE和WED |
/ |
增量。表示一个值的增量,e.g.分钟域中0/1表示从0开始,每次增加1min |
L |
表示Last。它在日期和星期域中表示有所不同。在日期域中,表示这个月的最后一天,而在星期域中,它永远是7(星期六)。当你希望使用星期中某一天时,L字符非常有用。e.g.星期域中6L表示每一个月的最后一个星期五 |
W |
在本月内离当天最近的工作日触发,所谓的最近工作日,即当天到工作日的前后最短距离,如果当天即为工作日,则距离是0;所谓本月内指的是不能跨月取到最近工作日,即使前/后月份的最后一天/第一天确实满足最近工作日。e.g. LW表示本月的最后一个工作日触发,W强烈依赖月份。 |
# |
表示该月的第几个星期,e.g. 1#2表示每一个月的第一个星期一 |
C |
日历值。日期值是根据一个给定的日历计算出来的。在日期域中给定一个20C将在20日(日历包括20日)或20日后日历中包含的第一天(不包括20日)激活触发器。例如在一个星期域中使用6C表示日历中星期五(日历包括星期五)或者第一天(日历不包括星期五) |
项目的依赖:
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.2.1</version>
</dependency>
在Spring中使用Quartz有两种方式实现:第一种是任务类继承QuartzJobBean,第二种则是在配置文件里定义任务类和要执行的方法,类和方法可以是普通类。很显然,第二种方式远比第一种方式来的灵活。
/**
* 定时器配置
*
*/
@Configuration
public class SpringTaskConfig {
/**
* 定时任务
*
* @return
*/
@Bean
public JobDetailFactoryBean jobDaySettleAgentMonthlyDetail() {
private static final String TASK_MONTHLY_CRON_EXPRESSION = "0 01 00 1 * ?";// 每月1日凌晨00:01分钟执行一次
JobDetailFactoryBean jobDetailFactoryBean = new JobDetailFactoryBean();
jobDetailFactoryBean.setJobClass(TaskMonthlyJob.class); //任务类
jobDetailFactoryBean.setDurability(true);
jobDetailFactoryBean.setRequestsRecovery(true);
return jobDetailFactoryBean;
}
@Bean
public CronTriggerFactoryBean settleAgentMonthlyTrigger() {
CronTriggerFactoryBean cronTriggerFactoryBean = new CronTriggerFactoryBean();
cronTriggerFactoryBean.setJobDetail(jobDaySettleAgentMonthlyDetail().getObject());
cronTriggerFactoryBean.setCronExpression(TASK_MONTHLY_CRON_EXPRESSION);
return cronTriggerFactoryBean;
}
}
任务类:
/**
* 定时任务
*
*/
@PersistJobDataAfterExecution
@DisallowConcurrentExecution
// 不允许并发执行
public class TaskMonthlyJob extends QuartzJobBean {
@Override
protected void executeInternal(JobExecutionContext jobexecutioncontext)
throws JobExecutionException {
*****任务代码****
}
}