Quartz是一个特性丰富的、开源的做业调度框架。它能够集成到任何Java应用。
使用它,你能够很是轻松的实现定时任务的调度执行。
场景1:提醒和告警
场景2:监听事务
场景3:定时做业
1.能够直接在官网:http://www.quartz-scheduler.org/ 下载jar包。
2.若是使用maven,能够在pom.xml中添加如下依赖jar包:
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.2.1</version>
</dependency>
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz-jobs</artifactId>
<version>2.2.1</version>
</dependency>
|
Github地址:https://github.com/quartz-scheduler/quartz
开始学习以前,惯例仍是show一下Hello World。
例:
1.先定义一个Job
import java.util.Date;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
public class HelloJob implements Job {
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
System.out.println(String.format("Hello World! Time:%s", new Date()));
}
}
|
2.定义Job和Trigger去调度咱们定义的HelloJob。
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerFactory;
import org.quartz.SimpleScheduleBuilder;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.impl.StdSchedulerFactory;
import org.zp.tent.scheduler.demo.job.HelloJob;
/**
* @Title HelloQuartz
* @Description Quartz的Hello World实例
* @Author zhangpeng
* @Date 2016年7月6日
*/
public class HelloWorldDemo {
public static void main(String[] args) {
try {
// 经过schedulerFactory获取一个调度器
SchedulerFactory schedulerfactory = new StdSchedulerFactory();
// 经过schedulerFactory获取一个调度器
Scheduler scheduler = schedulerfactory.getScheduler();
// 建立jobDetail实例,绑定Job实现类
JobDetail jobDetail = JobBuilder.newJob(HelloJob.class).withIdentity("helloJob", "jobGroup1").build();
// 定义调度触发规则,本例中使用SimpleScheduleBuilder建立了一个5s执行一次的触发器
Trigger trigger = TriggerBuilder.newTrigger().withIdentity("myTrigger", "triggerGroup1").startNow()
.withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(5).repeatForever())
.build();
// 把做业和触发器注册到任务调度中
scheduler.scheduleJob(jobDetail, trigger);
// 启动调度
scheduler.start();
// 60s后关闭
Thread.sleep(1000 * 30);
scheduler.shutdown();
System.out.println("调度任务结束");
} catch (Exception e) {
e.printStackTrace();
}
}
}
|
好了,运行一下试试吧。
做用:Scheduler接口是Quartz最核心的接口。Scheduler维护着JobDetail和Trigger的注册信息。一旦注册成功,Scheduler负责执行和Job关联的触发器。
一个Scheduler实例能够视为一个调度做业容器。能够经过start和shutdown方法来控制它的生命周期。
例:
// 经过schedulerFactory获取一个调度器
SchedulerFactory schedulerfactory = new StdSchedulerFactory();
// 经过schedulerFactory获取一个调度器
Scheduler scheduler = schedulerfactory.getScheduler();
// 启动
scheduler.start();
…
//关闭
scheduler.shutdown();
|
做用:开发者实现该接口定义须要执行的做业。JobExecutionContext类提供调度上下文的各类信息。
实现Job接口的类还可使用注解进行修饰。
@DisallowConcurrentExecution:此注解表示不容许这个Job并发执行
@PersistJobDataAfterExecution:此注解表示当这个Job的execute方法执行成功后,更新并存储它所持有的JobDetail属性中JobDataMap。若是使用这个注解,强烈建议也使用@DisallowConcurrentExecution,由于并发执行过程当中,JobDataMap有可能会发生冲突。
例:
public class xxxJob implements Job {
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
…
}
}
|
做用:用于定义Job实例。
JobDetail有两个boolean属性。
isDurable:若是设为false,则对应的Job一旦没有关联的触发器,就会被Scheduler自动删除。
requestsRecovery:若是设为true,当Job执行中遇到硬中断(例如运行崩溃、机器断电等),Scheduler会从新执行。这种状况下,JobExecutionContext.isRecovering()会返回ture。
做用:用于定义、构建JobDetail实例。
例:
// 建立jobDetail实例,绑定Job实现类
JobDetail jobDetail = JobBuilder.newJob(HelloJob.class).withIdentity("helloJob", "jobGroup1").build();
|
做用:定义Job执行的触发规则。
Quartz中有多种触发器,最经常使用的是SimpleTrigger 和 CronTrigger。
SimpleTrigger通常用于只执行一次或在指定时间执行的做业;CronTrigger通常用于周期性执行(例如,每日执行、每周执行)的做业,须要按照指定的时间表达式规则设置调度时间。
Priority:这个属性表示Trigger的权重。当两个Trigger触发时间相同时,权重大的那个先执行。Quartz默认的权重值为5。
Misfire Instruction:在Trigger接口中能够设置错过触发处理机制。就是说在指定触发的时间点因为某种缘由错过执行的时机了,这时如何去处理。Quartz提供了多种策略,这里不详述,有兴趣的能够参考官方文档。
Job和Trigger的关系
多个Job能够依赖于一个Trigger;多个Trigger也能够关联一个Job。
可是,从最佳实践来看,最好让Job和Trigger保持一对多的关系,这样更便于管理。
做用:用于定义、构建Trigger实例。
例:
下面两种方式是同样的效果,都是建立一个每5s执行一次的触发器
// 定义调度触发规则, SimpleScheduleBuilder方式
Trigger trigger = TriggerBuilder.newTrigger().withIdentity("myTrigger", "triggerGroup1").startNow().withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(5).repeatForever()).build();
// 定义调度触发规则, CronScheduleBuilder方式
Trigger trigger = TriggerBuilder.newTrigger().withIdentity("myTrigger", "triggerGroup1").startNow().withSchedule(CronScheduleBuilder.cronSchedule("0/5 * * * * ?")).build();
|
第二种触发器构建方式中使用了形如"0/5 * * * * ?"的CronExpression表达式来建立触发器规则。这里不在细说,在下文的CronExpression表达式一节再详述。
JobDetail接口中持有JobDataMap类。开发者能够将做业执行时须要的参数或对象填入这个类中。
填入数据和获取数据的方式很相似Json。
例:
先定义一个Job
public class WithJobDataMapJob implements Job {
public void execute(JobExecutionContext context) throws JobExecutionException {
// 基本信息
JobKey jobKey = context.getJobDetail().getKey();
TriggerKey triggerKey = context.getTrigger().getKey();
// 获取JobDataMap的方式:若是是基本类型,JobDataMap提供了多种get方法;若是是引用类型,能够直接get,而后进行强制转换
JobDataMap dataMap = context.getJobDetail().getJobDataMap();
Student student = (Student) dataMap.get("student");
List<String> interests = (List<String>) dataMap.get("interests");
String word = dataMap.getString("word");
System.out.println(String.format("[JobKey:%s][TriggerKey:%s] of DumbJob print info:", jobKey, triggerKey));
System.out.println(String.format("[Student]name:%s, age:%d, sex:%s", student.getName(), student.getAge(),
student.getSex()));
StringBuilder interestsStr = new StringBuilder();
for (String item : interests) {
interestsStr.append(item + " ");
}
System.out.println("His interests ars: " + interestsStr.toString());
System.out.println("He want to say: " + word);
System.out.println("===================================");
}
}
|
客户端代码:
public static void main(String[] args) {
try {
// 经过schedulerFactory获取一个调度器
SchedulerFactory schedulerfactory = new StdSchedulerFactory();
// 经过schedulerFactory获取一个调度器
Scheduler scheduler = schedulerfactory.getScheduler();
// 建立jobDetail实例,绑定Job实现类
JobDetail jobDetail = JobBuilder.newJob(WithJobDataMapJob.class).withIdentity("myJob", "group1").build();
// 使用JobDataMap填入想要携带的特殊信息。能够填入基本数据类型、字符串、集合,甚至是一个对象。填入方式很相似JSON
Student student = new Student("Jack", 20, "male");
List<String> interests = new ArrayList<String>();
interests.add("dancing");
interests.add("singing");
interests.add("swimming");
String word = "Hello World!";
JobDataMap map = jobDetail.getJobDataMap();
map.put("student", student);
map.put("interests", interests);
map.put("word", word);
// 定义调度触发规则,本例中使用SimpleScheduleBuilder建立了一个5s执行一次的触发器
Trigger trigger = TriggerBuilder.newTrigger().withIdentity("myTrigger", "triggerGroup1").startNow()
.withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(5).repeatForever())
.build();
// 把做业和触发器注册到任务调度中
scheduler.scheduleJob(jobDetail, trigger);
// 启动调度
scheduler.start();
// 60s后关闭
Thread.sleep(1000 * 30);
scheduler.shutdown();
System.out.println("调度任务结束");
} catch (Exception e) {
e.printStackTrace();
}
}
|
在Quartz中,能够分别经过JobKey和TriggerKey来惟一地识别一个Job或一个Trigger。
这两个Key都有两个关键属性:name和group。
Trigger trigger = TriggerBuilder.newTrigger().withIdentity("myTrigger", "triggerGroup1").startNow().withSchedule(CronScheduleBuilder.cronSchedule("0/5 * * * * ?")).build();
|
还记得上文中展现的使用CronScheduleBuilder方式构建触发器时的例子吗?在这个例子中,咱们使用的表达式字符串"0/5 * * * * ?"是什么意思呢?阅读本节后,你就会了解了。
一个cron表达式有至少6个(也可能7个)有空格分隔的时间元素。
CronTrigger配置完整格式为: [秒] [分] [小时] [日] [月] [周] [年]
参数设置规则见下表
字段
|
容许值
|
容许的特殊字符
|
秒
|
0-59
|
, - * /
|
分
|
0-59
|
, - * /
|
小时
|
0-23
|
, - * /
|
日期
|
1-31
|
, - * ? / L W
|
月份
|
1-12 或者 JAN-DEC
|
, - * /
|
星期
|
1-7 或者 SUN-SAT
|
, - * ? / L #
|
年(可选)
|
留空, 1970-2099
|
, - * /
|
表 cronExpression表达式参数
表示全部值。
例如:在分的字段上设置 "*",表示每一分钟都会触发。
表示不指定值。使用的场景为不须要关心当前设置这个字段的值。
例如:要在每个月的10号触发一个操做,但不关心是周几,因此须要周位置的那个字段设置为"?" 具体设置为 0 0 0 10 * ?
表示区间。
例如在小时上设置 "10-12",表示 10,11,12点都会触发。
表示指定多个值。
例如在周字段上设置 "MON,WED,FRI" 表示周一,周三和周五触发
用于递增触发。如在秒上面设置"5/15" 表示从5秒开始,每增15秒触发(5,20,35,50)。在月字段上设置'1/3'所示每个月1号开始,每隔三天触发一次。
表示最后的意思。
例如在日字段设置上,表示当月的最后一天(依据当前月份,若是是二月还会依据是不是润年[leap]), 在周字段上表示星期六,至关于"7"或"SAT"。若是在"L"前加上数字,则表示该数据的最后一个。例如在周字段上设置"6L"这样的格式,则表示“本月最后一个星期五"
表示离指定日期的最近那个工做日(周一至周五)。
例如在日字段上设置"15W",表示离每个月15号最近的那个工做日触发。若是15号正好是周六,则找最近的周五(14号)触发, 若是15号是周未,则找最近的下周一(16号)触发。若是15号正好在工做日(周一至周五),则就在该天触发。若是指定格式为 "1W",它则表示每个月1号日后最近的工做日触发。若是1号正是周六,则将在3号下周一触发。(注,"W"前只能设置具体的数字,不容许区间"-")。
小提示:'L'和 'W'能够一组合使用。若是在日字段上设置"LW",则表示在本月的最后一个工做日触发;周字段的设置,若使用英文字母是不区分大小写的,即MON与mon相同。
表示每个月的第几个周几。
例如在周字段上设置"6#3"表示在每个月的第三个周六。注意若是指定"#5",正好第五周没有周六,则不会触发该配置(用在母亲节和父亲节再合适不过了)。
注:表中月份一行的JAN-DEC,是指一月到十二月的英文缩写;星期一行的SUN-SAT,是指星期天到星期六的英文缩写。
案例
|
意义
|
"0 0 12 * * ?"
|
天天中午12点触发
|
"0 15 10 ? * *"
|
天天上午10:15触发
|
"0 15 10 * * ?"
|
天天上午10:15触发
|
"0 15 10 * * ? *"
|
天天上午10:15触发
|
"0 15 10 * * ? 2005"
|
2005年的天天上午10:15 触发
|
"0 * 14 * * ?"
|
在天天下午2点到下午2:59期间的每1分钟触发
|
"0 0/5 14 * * ?"
|
在天天下午2点到下午2:55期间的每5分钟触发
|
"0 0/5 14,18 * * ?"
|
在天天下午2点到2:55期间和下午6点到6:55期间的每5分钟触发
|
"0 0-5 14 * * ?"
|
在天天下午2点到下午2:05期间的每1分钟触发
|
"0 10,44 14 ? 3 WED"
|
每一年三月的星期三的下午2:10和2:44触发
|
"0 15 10 ? * MON-FRI"
|
周一至周五的上午10:15触发
|
"0 15 10 15 * ?"
|
每个月15日上午10:15触发
|
"0 15 10 L * ?"
|
每个月最后一日的上午10:15触发
|
"0 15 10 ? * 6L"
|
每个月的最后一个星期五上午10:15触发
|
"0 15 10 ? * 6L 2002-2005"
|
2002年至2005年的每个月的最后一个星期五上午10:15触发
|
"0 15 10 ? * 6#3"
|
每个月的第三个星期五上午10:15触发
|
官方文档:http://www.quartz-scheduler.org/documentation/
官方2.2版本教程:http://www.quartz-scheduler.org/documentation/quartz-2.2.x/tutorials/