1.Quartz简介
Quartz是一个企业任务日程调度管理组件,无论是互联网公司还是传统的软件行业都是必不可少的,Quartz是好多优秀的定时任务开源框架的基础,很有必要去掌握,只需要掌握少量参数,就可以掌握企业中的定时任务处理。
2.Quartz概念
Quartz是OpenSymphony开源组织在Job scheduling领域的一个开源项目,它可以与J2EE和J2SE应用程序结合也可以单独使用,Quartz是开源且具有丰富特性的“任务调度库”,能够集成于任何的java应用,小到独立的应用,大到电子商业系统,quartz能够创建简单或复杂的任务调度,以执行上十,上百,甚至上万的任务,任务job被定义为标准的java组件,能够执行任何你想要实现的功能,quartz调度框架包含许多企业级的特性,如JTA事物,集群的支持,简而言之,quartz就是基于java实现的任务调度框架,用于执行你想执行的任何任务
官网:
http://www.quartz-scheduler.org/
3.Quartz运行环境
- Quartz可以运行嵌入在另一个独立式应用程序
- Quartz可以在应用程序服务器(或servlet容器)内被实例化,并且参与事务
- Quartz可以作为一个独立的程序运行(其自己的java虚拟机内),可以通过RMI使用
- Quartz可以被实例化,作为独立的项目集群(负载均衡和故障转移功能),用于作业的执行
4.Quartz的设计模式
- Builder模式
- Factory模式
- 组件模式
- 链式编程
5.Quartz核心概念
- 任务job
job就是你想要实现的任务类,每一个job必须实现org,quartz.job接口,且只需实现接口定义的execute(方法) - 触发器Trigger
Trigger为你执行任务的触发器,比如你每天定时3点发送一份统计邮件,Trigger将会设置3点进行执行该任务。Trigger主要包含两种Simple Trigger和Cron Trigger两种,关于二者的区别的使用场景,后续的课程会进行讨论。 - 调度器Scheduler
Scheduler为任务的调度器,它会将任务job及触发器Trigger整合起来,负责基于Trigger设定的时间来执行job
6.Quartz的体系结构
7.Quartz的几个常用API
以下是Quartz编程API几个重要接口,也是Quartz的重要组件
- Scheduler 用于与调度程序交互的主程序接口
Scheduler调度程序-任务执行计划表,只有安排进执行计划的任务job(通过scheduler.schedulerjob方法安排进执行计划),当它预先定义的执行时间到了的时候(任务触发trigger),该任务才会执行。 - Job 我们预先定义的希望在未来时间内被调度程序执行的任务类,我们可以自定义。
- JobDetail 使用JobDetail来定义定时任务的实例,JobDetail实例是通过JobBuilder类创建的。
- JobDataMap 可以包含不限量的(序列化的)数据对象,在job实例执行的时候,可以使用其中的数据,JobDataMap是java Map接口的一个实现,额外增加了一些便于存取基本类型的数据方法。
- Trigger触发器 Trigger对象是用来触发执行job的,当调度一个job时,我们实例一个触发器然后调度它的属性来满足job执行的条件。表明任务在什么时候会执行,定义了一个已经被安排的任务将会在什么时候执行的时间条件,比如每2秒就执行一次
- JobBuilder 用于声明一个任务实例,也可以定义关于该任务的详情比如任务名、组件等,这个声明的实例将会作为一个实际执行的任务。
- TriggerBuilder 触发器创建器,用于创建触发器trigger实例。
- JobListener、TriggerListener、SchedulerListenner监听器,用于对组件的监听
7.1 Job和JobDetail介绍
- Job :工作任务调度的接口,任务类需要实现该接口,该接口中定义execute方法,类似JDK提供的TimeTask类的run方法,在里面编写任务执行的业务逻辑
- Job实例在Quartz中的生命周期:每次调度器执行job时,它在调用execute方法前会创建一个新的job实例,当调用完成后,关联的Job对象实例会被释放,释放的实例会被垃圾回收机制回收。
- JobDetail:JobDetail为Job实例提供了许多设置属性,以及JobDataMap成员变量属性,它用来存贮特定Job实例的状态信息,调度器需要借助JobDetail对象来添加Job实例。
- JobDetail重要属性:name、group、jobClass、jobDataMap
//创建JobDetail(作业信息)
JobDetail jb = JobBuilder.newJob(MyJob.class)
.withDescription("this is a test job") //job的描述
.withIdentity("testJob", "testGroup") //job 的name和group
.build();
7.2 JobExecutionContext介绍
- 当Scheduler调用一个Job,就会将JobExecutionContext传递给Job的execute()方法
- Job能通过JobExecutionContext对象访问到Quartz运行时候的环境以及Job本身的明细数据
7.3 JobDataMap介绍
- 在进行任务调度时,JobDataMap存储在JobExecutionContext中,非常方便获取
- JobDataMap可以用来装在任何可序列化的数据对象,当job实例对象被执行时这些参数对象会传递给它
- JobDataMap实现了JDK的Map接口,并且添加了非常方便的方法用来存取基本数据类型
//3.创建JobDetail(作业信息)
JobDetail jb = JobBuilder.newJob(MyJob.class)
.withDescription("this is a test job") //job的描述
.withIdentity("testJob", "testGroup") //job 的name和group
.build();
//4.创建Trigger
//使用SimpleScheduleBuilder或者CronScheduleBuilder
Trigger t = TriggerBuilder.newTrigger()
.withDescription("")
.withIdentity("ramTrigger", "ramTriggerGroup")
.startAt(statTime) //默认当前时间启动
//普通计时器
//.withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(3).withRepeatCount(3))//间隔3秒,重复3次
//表达式计时器
.withSchedule(CronScheduleBuilder.cronSchedule("0/3 * * * * ?")) //3秒执行一次
.build();
- Job实现类中添加setter方法对应jobDataMap的键值,Quartz框架默认的jobFactory实现类在初始化job实例对象时会自动的调用这些setter方法
private String message;
public void setMessage(String message) {
this.message = message;
}
- 这里注意:如果遇到同名的key,Trigger中的.usingJobData(“message”,“simple触发器”)会覆盖JobDetail中的.usingJobData(“message”,“打印日志”)。
7.4 有状态的Job和无状态的Job
@PersistJobDataAfterExecution注解的使用
- 有状态的Job可以理解为多次Job调用期间可以持有一些状态信息,这些状态信息存储在JobDataMap中,而默认的无状态Job每次调用时都会创建一个新的jobDataMap.
- 具体代码实现可以初始化一个count变量设为自增来计数,使用注解和不使用注解分别打印一下count的值,看是否是新建的job
8. Trigger详解
Quartz有一些不同的触发器类型,不过,用得最多的是SimpleTrigger和CronTrigger
- jobKey
表示job实例的标识,触发器被触发时,该指定的job实例会被执行, - startTime
表示触发器的时间表,第一次开始被触发的时间,它的数据类型是java.util.Data - endTime
指定触发器终止被触发的时间,它的数据类型是java.util.Data.
案例:
public class DemoQuartz {
public static void main(String[] args) throws Exception {
//1.创建Scheduler的工厂
SchedulerFactory sf = new StdSchedulerFactory();
//2.从工厂中获取调度器实例
Scheduler scheduler = sf.getScheduler();
Date stdate = new Date();
stdate.setTime(stdate.getTime());
Date enddate = new Date();
enddate.setTime(enddate.getTime()+10000);
//3.创建JobDetail(作业信息)
JobDetail jb = JobBuilder.newJob(MyJob.class)
.withDescription("this is a test job") //job的描述
.withIdentity("testJob", "testGroup") //job 的name和group
.usingJobData("message","打印日志")
.build();
//向任务传递数据
JobDataMap jobDataMap = jb.getJobDataMap();
jobDataMap.put("JobDataMap传参", "测试");
/*//任务运行的时间,SimpleSchedle类型触发器有效
long time = System.currentTimeMillis() + 3 * 1000L; //3秒后启动任务
Date statTime = new Date(time);*/
//4.创建Trigger
//使用SimpleScheduleBuilder或者CronScheduleBuilder
Trigger t = TriggerBuilder.newTrigger()
.withDescription("")
.withIdentity("ramTrigger", "ramTriggerGroup")
.startAt(stdate) //默认当前时间启动
.endAt(enddate)//结束时间
//普通计时器
//.withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(3).withRepeatCount(3))//间隔3秒,重复3次
//表达式计时器
.withSchedule(CronScheduleBuilder.cronSchedule("0/3 * * * * ?")) //3秒执行一次
.usingJobData("message","simple触发器")
.build();
//5.注册任务和定时器
scheduler.scheduleJob(jb, t);
//6.启动 调度器
scheduler.start();
}
}
public class MyJob implements Job {
private String message;
public void setMessage(String message) {
this.message = message;
}
public void execute(JobExecutionContext context) throws JobExecutionException {
System.out.println("定时器任务执行,当前时间为:" + new Date(System.currentTimeMillis()));
Date date = new Date();
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dateString = dateFormat.format(date);
//System.out.println("执行定时任务,当前时间为:"+dateString);
Trigger trigger = context.getTrigger();
System.out.println("jobKey标识:"+trigger.getJobKey().getName()+
"jobKey组名称"+trigger.getJobKey().getGroup());
System.out.println("开始时间:"+dateFormat.format(trigger.getStartTime())+
"结束时间:"+dateFormat.format(trigger.getEndTime()));
JobDataMap map=context.getMergedJobDataMap();
System.out.println("JobDataMap参数值:"+map.get("JobDataMap传参"));
}
}
9. SimpleTrigger触发器
- SimpleTrigger对于设置和使用是最为简单的一种QuartzTrigger。它是为那种需要在特定的日期/时间启动,且以一个可能的时间间隔时间重复执行n次的job所设计的。
案例就省略掉了,你们自己可以尝试修改参数 - 需要注意的点
- SimpleTrigger的属性有:开始时间、结束时间、重复次数和重复的时间间隔
- 重复次数属性的值可以为0,正整数,或常量SimpleTrigger.REPEAT_INDEFINITELY
- 重复的时间间隔属性值必须为0或长整型的正整数,以毫秒作为时间单位,当重复的时间间隔为0时,意味着与Trigger同时触发执行
- 如果有指定结束时间属性值,则结束时间属性优先于重复次数属性,这样的好处在于:当我们需要创建一个每间隔十秒钟触发一次直到指定的结束时间的Trigger,而无需去计算从开始到结束的所重复的次数,我们只需简单的指定结束时间和使用REPEAT_INDEFINITELY作为重复次数的属性值即可。
10. CrinTrigger触发器
- 如果你需要像日历那样按照日程来触发任务,而不是像SimpleTrigger那样每隔特定的时间间隔触发,CrinTriggers通常比SimpleTrigger更有用,因为它是基于日历的作业调度器。使用CrinTrigger,你可以指定诸如"每个周五中午",或者“每个工作日的9:30”或"从每周一周三周五的上午9:00到上午10:00之间每隔五分钟"这样日程安排来触发,甚至像SimpleTrigger一样,CrinTrigger也有一个starttime以指定日程从什么时候开始,也有一个可选的endtime指定何时日程不在继续。
10.1 Crin Expressions–Cron表达式
- Cron表达式被用来配置CrinTrigger实例,Cron表达式是一个由7个子表达式组成的字符串,每个子表达式都描述了一个单独的日程细节,这些子表达式用空格分隔,分别表示:
- Seconds 秒
- Minutes 分钟
- Hours 小时
- Day-of-Month 月中的天
- Month 月
- Day-of-Week 周中的天
- Year 年(可选的域)
取值为:
- 所有的域中的值都有特定的合法范围,这些值的合法范围相当明显,就不用了举例了
- 符号解释如下:
举例如下:
- 可以在网页中的在线转换工具在线转换你想要的定时任务表达式
未完待续…