通过Quartz实现定时任务
1. Quartz 概述
Quartz 是 OpenSymphony 开源组织在任务调度领域的一个开源项目,完全基于 Java 实现。该项目于 2009 年被 Terracotta 收购,目前是 Terracotta 旗下的一个项目。读者可以到 http://www.quartz-scheduler.org/站点下载 Quartz 的发布版本及其源代码。
2. Quartz特点
作为一个优秀的开源调度框架,Quartz 具有以下特点:
-
强大的调度功能,例如支持丰富多样的调度方法,可以满足各种常规及特殊需求;
-
灵活的应用方式,例如支持任务和调度的多种组合方式,支持调度数据的多种存储方式;
-
分布式和集群能力,Terracotta 收购后在原来功能基础上作了进一步提升。
另外,作为 Spring 默认的调度框架,Quartz 很容易与 Spring 集成实现灵活可配置的调度功能。
quartz调度核心元素:
- Scheduler:任务调度器,是实际执行任务调度的控制器。在spring中通过SchedulerFactoryBean封装起来。
- Trigger:触发器,用于定义任务调度的时间规则,有SimpleTrigger,CronTrigger,DateIntervalTrigger和NthIncludedDayTrigger,其中CronTrigger用的比较多,本文主要介绍这种方式。CronTrigger在spring中封装在CronTriggerFactoryBean中。
- Calendar:它是一些日历特定时间点的集合。一个trigger可以包含多个Calendar,以便排除或包含某些时间点。
- JobDetail:用来描述Job实现类及其它相关的静态信息,如Job名字、关联监听器等信息。在spring中有JobDetailFactoryBean和 MethodInvokingJobDetailFactoryBean两种实现,如果任务调度只需要执行某个类的某个方法,就可以通过MethodInvokingJobDetailFactoryBean来调用。
- Job:是一个接口,只有一个方法void execute(JobExecutionContext context),开发者实现该接口定义运行任务,JobExecutionContext类提供了调度上下文的各种信息。Job运行时的信息保存在JobDataMap实例中。实现Job接口的任务,默认是无状态的,若要将Job设置成有状态的,在quartz中是给实现的Job添加@DisallowConcurrentExecution注解(以前是实现StatefulJob接口,现在已被Deprecated),在与spring结合中可以在spring配置文件的job detail中配置concurrent参数。
3. Quartz基本原理
核心元素
Quartz 任务调度的核心元素是 scheduler(调度器), trigger(触发器) 和 job(作业),其中 trigger 和 job 是任务调度的元数据, scheduler 是实际执行调度的控制器。
在 Quartz 中,trigger 是用于定义调度时间的元素,即按照什么时间规则去执行任务。Quartz 中主要提供了四种类型的 trigger:SimpleTrigger,CronTirgger,DateIntervalTrigger,和 NthIncludedDayTrigger。这四种 trigger 可以满足企业应用中的绝大部分需求。我们将在企业应用一节中进一步讨论四种 trigger 的功能。
在 Quartz 中,job 用于表示被调度的任务。主要有两种类型的 job:无状态的(stateless)和有状态的(stateful)。对于同一个 trigger 来说,有状态的 job 不能被并行执行,只有上一次触发的任务被执行完之后,才能触发下一次执行。Job 主要有两种属性:volatility 和 durability,其中 volatility 表示任务是否被持久化到数据库存储,而 durability 表示在没有 trigger 关联的时候任务是否被保留。两者都是在值为 true 的时候任务被持久化或保留。一个 job 可以被多个 trigger 关联,但是一个 trigger 只能关联一个 job。
在 Quartz 中, scheduler 由 scheduler 工厂创建:DirectSchedulerFactory 或者 StdSchedulerFactory。 第二种工厂 StdSchedulerFactory 使用较多,因为 DirectSchedulerFactory 使用起来不够方便,需要作许多详细的手工编码设置。 Scheduler 主要有三种:RemoteMBeanScheduler, RemoteScheduler 和 StdScheduler。本文以最常用的 StdScheduler 为例讲解。这也是笔者在项目中所使用的 scheduler 类。
Quartz 核心元素之间的关系如下图所示:
4.Quartz使用
1.pom.xml导入Quartz依赖或源码下载
<!-- Quartz 依赖 -->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>latest-x.y.z</version>
</dependency>
<!--以下选择性导入-->
<!-- Quartz使用SLF4J,因此我们需要一个实际的记录器 -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<!-- 数据库驱动程序,如果您选择使用PostgreSQL作为Quartz JDBCStore -->
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.2.5</version>
</dependency>
2.接下来浏览下面的代码片段,它实例化和启动了一个调度器,并且调度执行了一个Job对象。
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import static org.quartz.JobBuilder.newJob;
import static org.quartz.SimpleScheduleBuilder.simpleSchedule;
import static org.quartz.TriggerBuilder.newTrigger;
public class SchedulerTest {
public static void main(String[] args) throws SchedulerException {
//创建一个调度器工厂
SchedulerFactory schedFact = new StdSchedulerFactory();
//得到一个调度器
Scheduler sched = schedFact.getScheduler();
//启动调度器
sched.start();
//定义工作并将其绑定到HelloJob类
JobDetail job = newJob(HelloJob.class)
.withIdentity("myJob", "group1")
.build();
//创建触发器,触发作业现在运行,然后每1秒运行一次
Trigger trigger = newTrigger()
.withIdentity("myTrigger", "group1")
.startNow()
.withSchedule(simpleSchedule()
.withIntervalInSeconds(1)
.repeatForever())
.build();
//告诉quartz使用我们的触发器调度作业
sched.scheduleJob(job, trigger);
}
}
定义的工作,只需继承Job
类重写execute
方法
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class HelloJob implements Job {
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
//获取当前时间
Date data=new Date();
//定义时间输出格式
SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//转换格式
String currentTime = simpleDateFormat.format(data);
System.out.println(currentTime);
}
}
5.Quartz API,Jobs和Triggers
1.Quartz API的关键接口:
- Scheduler - 与调度程序交互的主要API。
- Job - 你想要调度器执行的任务组件需要实现的接口
- JobDetail - 用于定义作业的实例。
- Trigger(即触发器) - 定义执行给定作业的计划的组件。
- JobBuilder - 用于定义/构建 JobDetail 实例,用于定义作业的实例。
- TriggerBuilder - 用于定义/构建触发器实例。
- Scheduler 的生命期,从 SchedulerFactory 创建它时开始,到 Scheduler 调用shutdown() 方法时结束;Scheduler 被创建后,可以增加、删除和列举 Job 和 Trigger,以及执行其它与调度相关的操作(如暂停 Trigger)。但是,Scheduler 只有在调用 start() 方法后,才会真正地触发 trigger(即执行 job)
Quartz 提供的“builder”类,可以认为是一种领域特定语言(DSL,Domain Specific Language)。DSL 的静态导入可以通过以下导入语句来实现:
import static org.quartz.JobBuilder.*;
import static org.quartz.SimpleScheduleBuilder.*;
import static org.quartz.CronScheduleBuilder.*;
import static org.quartz.CalendarIntervalScheduleBuilder.*;
import static org.quartz.TriggerBuilder.*;
import static org.quartz.DateBuilder.*;
2.Job 和 Trigger
一个 job 就是一个实现了 Job 接口的类,该接口只有一个方法:execute(JobExecutionContext context)
当 Job 的一个 trigger 被触发时,execute() 方法由调度程序的一个工作线程调用。
传递给 execute() 方法的 JobExecutionContext 对象向作业实例提供有关其“运行时”环job的一个 trigger 被触发后,execute() 方法会被 scheduler 的一个工作线程调用;
传递给 execute() 方法的 JobExecutionContext 对象中保存着该 job 运行时的一些信息 ,执行 job 的 scheduler 的引用,触发 job 的 trigger 的引用,JobDetail 对象引用,以及一些其它信息。
6.Job与JobDetail介绍
1.JobDetail(工作细节)
你定义了一个实现Job接口的类,这个类仅仅表明该job需要完成什么类型的任务,除此之外,Quartz还需要知道该Job实例所包含的属性;这将由JobDetail类来完成。
JobDetail job = newJob(HelloJob.class)
.withIdentity("myJob", "group1")
.build();
可以看到,我们传给scheduler一个JobDetail实例,因为我们在创建JobDetail时,将要执行的job的类名传给了JobDetail,所以scheduler就知道了要执行何种类型的job;每次当scheduler执行job时,在调用其execute(…)方法之前会创建该类的一个新的实例;执行完毕,对该实例的引用就被丢弃了,实例会被垃圾回收;
2.JobDataMap(工作数据图)
在job类中,不应该定义有状态的数据属性,因为在job的多次执行中,这些属性的值不会保留。
那么如何给job实例增加属性或配置呢?如何在job的多次执行中,跟踪job的状态呢?答案就是:JobDataMap,JobDetail对象的一部分。
代码操作如下:
创建DtlJob类实现Job接口
import org.quartz.*;
/**
* 测试JobDataMap
*/
public class DtlJob implements Job {
public DtlJob() {
}
@Override
public void execute(JobExecutionContext context)
throws JobExecutionException {
//通过Job上下文对象获取JobDetail拿到设置的身份
JobKey key = context.getJobDetail().getKey();
//这里通过Job上下文对象得到JobDataMap对象
JobDataMap dataMap = context.getJobDetail().getJobDataMap();
//JobDataMap对象通过设置的key去拿value
String jobSays = dataMap.getString("jobSays");
float myFloatValue = dataMap.getFloat("myFloatValue");
System.err.println("当前分组是 " + key + "JobDataMap说" + jobSays + ", and val is: " + myFloatValue);
}
}
创建JobDataMapTest
类测试
import com.zgf.job.DtlJob;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import static org.quartz.JobBuilder.newJob;
import static org.quartz.SimpleScheduleBuilder.simpleSchedule;
import static org.quartz.TriggerBuilder.newTrigger;
/**
* @Author 渣高帆
* @Date 2020/9/8 15:11
* @Version 1.0
* 作用: JobDataMap(工作数据图)测试
**/
public class JobDataMapTest {
public static void main(String[] args) throws SchedulerException {
//创建一个调度器工厂
SchedulerFactory schedulerFactory=new StdSchedulerFactory();
//获取调度器
Scheduler scheduler = schedulerFactory.getScheduler();
//启动调度器
scheduler.start();
// 定义工作并将其绑定到我们的DtlJob类
JobDetail job = newJob(DtlJob.class)
// 名称“ myJob”,组“ group1”
.withIdentity("myJob", "group1")
//设置key-value,value可以为Boolean、Double、Integer、float、long、String类型
.usingJobData("jobSays", "Hello World!")
.usingJobData("myFloatValue", 3.141f)
//开始建造
.build();
//创建触发器
Trigger trigger= newTrigger()
//身份设置
.withIdentity("trigger1","trigger")
//现在开始触发
.startNow()
//附表
.withSchedule(
//时间表
simpleSchedule()
//一秒钟触发
.withIntervalInSeconds(1)
//永久重复
.repeatForever()
)
//开始建造
.build();
scheduler.scheduleJob(job,trigger);
}
}
7.Quartz中Triggers介绍
和Job一样,trigger也很容易使用,但是还有一些扩展选项需要理解,以便更好地使用quartz。trigger也有很多类型,我们可以根据实际需要来选择。
1.Trigger的公共属性
所有类型的Trigger都有TriggerKey这个属性,表示trigger的身份;除此之外,trigger还有很多其它的公共属性。这些属性,在构建trigger的时候可以通过TriggerBuilder设置。
Trigger的公共属性有:
- jobKey属性:当trigger触发时被执行的job的身份;
- startTime属性:设置trigger第一次触发的时间;该属性的值是java.util.Date类型,表示某个指定的时间点;有些类型的trigger,会在设置的startTime时立即触发,有些类型的trigger,表示其触发是在startTime之后开始生效。比如,现在是1月份,你设置了一个trigger–“在每个月的第5天执行”,然后你将startTime属性设置为4月1号,则该trigger第一次触发会是在几个月以后了(即4月5号)。
- endTime属性:表示trigger失效的时间点。比如,”每月第5天执行”的trigger,如果其endTime是7月1号,则其最后一次执行时间是6月5号。
2.优先级(priority)
如果你的trigger很多(或者Quartz线程池的工作线程太少),Quartz可能没有足够的资源同时触发所有的trigger;这种情况下,你可能希望控制哪些trigger优先使用Quartz的工作线程,要达到该目的,可以在trigger上设置priority属性。比如,你有N个trigger需要同时触发,但只有Z个工作线程,优先级最高的Z个trigger会被首先触发。如果没有为trigger设置优先级,trigger使用默认优先级,值为5;priority属性的值可以是任意整数,正数、负数都可以。
注意:只有同时触发的trigger之间才会比较优先级。10:59触发的trigger总是在11:00触发的trigger之前执行。
注意:如果trigger是可恢复的,在恢复后再调度时,优先级与原trigger是一样的。
3.错过触发(misfire Instructions)
trigger还有一个重要的属性misfire;如果scheduler关闭了,或者Quartz线程池中没有可用的线程来执行job,此时持久性的trigger就会错过(miss)其触发时间,即错过触发(misfire)。不同类型的trigger,有不同的misfire机制。它们默认都使用“智能机制(smart policy)”,即根据trigger的类型和配置动态调整行为。当scheduler启动的时候,查询所有错过触发(misfire)的持久性trigger。然后根据它们各自的misfire机制更新trigger的信息。当你在项目中使用Quartz时,你应该对各种类型的trigger的misfire机制都比较熟悉;
4.日历(calendar)示例
Quartz的Calendar对象(不是java.util.Calendar对象)可以在定义和存储trigger的时候与trigger进行关联。Calendar用于从trigger的调度计划中排除时间段。比如,可以创建一个trigger,每个工作日的上午9:30执行,然后增加一个Calendar,排除掉所有的商业节日。
任何实现了Calendar接口的可序列化对象都可以作为Calendar对象,Calendar接口如下:
package org.quartz;
public interface Calendar {
public boolean isTimeIncluded(long timeStamp);
public long getNextIncludedTime(long timeStamp);
}
注意到这些方法的参数类型为long。你也许猜到了,他们就是毫秒单位的时间戳。即Calendar排除时间段的单位可以精确到毫秒。你也许对“排除一整天”的Calendar比较感兴趣。Quartz提供的org.quartz.impl.HolidayCalendar类可以很方便地实现。
Calendar必须先实例化,然后通过addCalendar()方法注册到scheduler。如果使用HolidayCalendar,实例化后,需要调用addExcludedDate(Date date)方法从调度计划中排除时间段。以下示例是将同一个Calendar实例用于多个trigger:
Calendar
实现类HolidayCalendar
的使用
import com.zgf.job.MailJob;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import org.quartz.impl.calendar.HolidayCalendar;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import static org.quartz.CronScheduleBuilder.dailyAtHourAndMinute;
import static org.quartz.JobBuilder.newJob;
import static org.quartz.TriggerBuilder.newTrigger;
public class CalendarTest {
public static void main(String[] args) throws SchedulerException, ParseException {
SchedulerFactory schedulerFactory=new StdSchedulerFactory();
Scheduler scheduler = schedulerFactory.getScheduler();
scheduler.start();
//假期日历对象
HolidayCalendar cal = new HolidayCalendar();
//因为JDK8的Date(String)已经过时,这里使用日期格式化类
SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyy-MM-dd");
//通过Sparse
Date date=simpleDateFormat.parse("2020-09-15");
//添加假期时间,可以添加多个假期,通过cal.addExcludedDate(Date date)添加
cal.addExcludedDate(date);
//调度器添加日历
scheduler.addCalendar("myHolidays", cal, false,true);
// 定义工作并将其绑定到我们的DtlJob类
JobDetail job = newJob(MailJob.class)
// 名称“ myJob”,组“ group1”
.withIdentity("myJob", "group1")
//开始建造
.build();
Trigger t = newTrigger()
.withIdentity("myTrigger")
.startNow()
.withSchedule(
//每天17:30执行工作
dailyAtHourAndMinute(17, 30)
)
// 设置的节假日除外,可以设置多个节假日,在后面.modifiedByCalendar("calName")就行了
.modifiedByCalendar("myHolidays")
.build();
scheduler.scheduleJob(job, t);
}
}
5.SimpleTrigger(简单触发器)以及触发器源码解读
SimpleTrigger可以满足的调度需求是:在具体的时间点执行一次,或者在具体的时间点执行,并且以指定的间隔重复执行若干次。比如,你有一个trigger,你可以设置它在2015年1月13日的上午11:23:54准时触发,或者在这个时间点触发,并且每隔2秒触发一次,一共重复5次。
根据描述,你可能已经发现了,SimpleTrigger的属性包括:开始时间、结束时间、重复次数以及重复的间隔。这些属性的含义与你所期望的是一致的,只是关于结束时间有一些地方需要注意。
重复次数,可以是0、正整数,以及常量SimpleTrigger.REPEAT_INDEFINITELY。重复的间隔,必须是0,或者long型的正数,表示毫秒。注意,如果重复间隔为0,trigger将会以重复次数并发执行(或者以scheduler可以处理的近似并发数)。
下面的例子,是基于简单调度(simple schedule)创建的trigger。建议都看一下,因为每个例子都包含一个不同的实现点:
指定时间开始触发,不重复:
import com.zgf.job.MailJob;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import org.quartz.impl.triggers.SimpleTriggerImpl;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import static org.quartz.JobBuilder.newJob;
import static org.quartz.TriggerBuilder.newTrigger;
/**
* @Author 渣高帆
* @Date 2020/9/9 0:19
* @Version 1.0
* 简单触发器的使用
**/
public class SimpleTriggerTest {
public static void main(String[] args) throws ParseException, SchedulerException {
//创建一个调度器工厂
SchedulerFactory schedFact = new StdSchedulerFactory();
//得到一个调度器
Scheduler sched = schedFact.getScheduler();
//启动调度器
sched.start();
//定义工作并将其绑定到HelloJob类
JobDetail job = newJob(MailJob.class)
.withIdentity("myJob", "group1")
.build();
//创建一个简单触发器实现类
SimpleTriggerImpl trigger = new SimpleTriggerImpl()
//安排工作
sched.scheduleJob(job, trigger);
}
}
SimpleTriggerImpl
简单触发器实现类源码解读,getter/setter方法直接看属性上的注释。
public class SimpleTriggerImpl extends AbstractTrigger<SimpleTrigger> implements SimpleTrigger, CoreTrigger {
private static final long serialVersionUID = -3735980074222850397L;
private static final int YEAR_TO_GIVEUP_SCHEDULING_AT = java.util.Calendar.getInstance().get(java.util.Calendar.YEAR) + 100;
//这些私有属性都使用get/set方法进行了封装
//设置或得到触发器的开始时间
private Date startTime = null;
//设置或得到触发器的结束时间
private Date endTime = null;
//设置或得到下一次触发时间
private Date nextFireTime = null;
//设置或得到上一次的触发时间
private Date previousFireTime = null;
//设置或得到触发器的触发次数,必须为正数否则报IllegalArgumentException
private int repeatCount = 0;
//设置或得到触发器的触发时间间隔(以毫秒为单位)
private long repeatInterval = 0;
//设置或得到触发器已被触发的次数。
private int timesTriggered = 0;
//触发器是否完成
private boolean complete = false;
/**
* 创建一个简单触发器的无参构造
*/
public SimpleTriggerImpl() {
super();
}
/**
* 创建将立即发生的触发器,并且不再重复。
* @Param name 给触发器设定一个名字
*/
@Deprecated
public SimpleTriggerImpl(String name) {
this(name, (String)null);
}
/**
* 创建将立即发生的触发器,并且不再重复。
* @Param name 给触发器设定一个名字
* @Param group 这个触发器在哪个分组内
*/
@Deprecated
public SimpleTriggerImpl(String name, String group) {
this(name, group, new Date(), null, 0, 0);
}
/**
* 创建将立即发生的触发器,并且设置触发次数和触发时间间隔
* @Param name 设置触发器名称
* @Param repeatCount 设置触发器触发次数,正数
* @Param repeatInterval 设置触发间隔时间,以毫秒为单位
*/
@Deprecated
public SimpleTriggerImpl(String name, int repeatCount, long repeatInterval) {
this(name, null, repeatCount, repeatInterval);
}
/**
* 创建将立即发生的触发器,并且设置触发次数和触发时间间隔
* @Param name 设置触发器名称
* @Param group 设置触发器的分组
* @Param repeatCount 设置触发器触发次数,正数
* @Param repeatInterval 设置触发间隔时间,以毫秒为单位
*/
@Deprecated
public SimpleTriggerImpl(String name, String group, int repeatCount,
long repeatInterval) {
this(name, group, new Date(), null, repeatCount, repeatInterval);
}
/**
* 在指定时间触发,不重复
* @Param name 设置触发器名称
* @Param startTime 触发时间
*/
@Deprecated
public SimpleTriggerImpl(String name, Date startTime) {
this(name, null, startTime);
}
/**
* 在指定时间触发,不重复
* @Param name 设置触发器名称
* @Param group 设置触发器的分组
* @Param startTime 触发时间
*/
@Deprecated
public SimpleTriggerImpl(String name, String group, Date startTime) {
this(name, group, startTime, null, 0, 0);
}
/**
* 创建一个将在指定时间发生的触发器,可以设置触发次数、间隔时间、结束时间
* @param name 触发器名称
* @param startTime 开始时间
* @param endTime 结束时间
* @param repeatCount 触发次数,正数
* @param repeatInterval 间隔时间,毫秒为单位
*/
@Deprecated
public SimpleTriggerImpl(String name, Date startTime,
Date endTime, int repeatCount, long repeatInterval) {
this(name, null, startTime, endTime, repeatCount, repeatInterval);
}
/**
* 创建一个将在指定时间发生的触发器,可以设置触发次数、间隔时间、结束时间
* @param name 触发器名称
* @param group 触发器分组
* @param startTime 开始时间
* @param endTime 结束时间
* @param repeatCount 触发次数,正数
* @param repeatInterval 间隔时间,毫秒为单位
*/
@Deprecated
public SimpleTriggerImpl(String name, String group, Date startTime,
Date endTime, int repeatCount, long repeatInterval) {
super(name, group);
setStartTime(startTime);
setEndTime(endTime);
setRepeatCount(repeatCount);
setRepeatInterval(repeatInterval);
}
/**
* 创建一个将在指定时间发生的触发器,可以设置指定触发的Job(作业)触发次数、间隔时间、结束时间
* @param name 触发器名称
* @param group 触发器分组
* @param startTime 开始时间
* @param endTime 结束时间
* @param repeatCount 触发次数,正数
* @param repeatInterval 间隔时间,毫秒为单位
*/
@Deprecated
public SimpleTriggerImpl(String name, String group, String jobName,
String jobGroup, Date startTime, Date endTime, int repeatCount,
long repeatInterval) {
super(name, group, jobName, jobGroup);
setStartTime(startTime);
setEndTime(endTime);
setRepeatCount(repeatCount);
setRepeatInterval(repeatInterval);
}
/** getter/setter方法分割线
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@Override
public Date getStartTime() {
return startTime;
}
@Override
public void setStartTime(Date startTime) {
if (startTime == null) {
throw new IllegalArgumentException("Start time cannot be null");
}
Date eTime = getEndTime();
if (eTime != null && startTime != null && eTime.before(startTime)) {
throw new IllegalArgumentException(
"End time cannot be before start time");
}
this.startTime = startTime;
}
@Override
public Date getEndTime() {
return endTime;
}
@Override
public void setEndTime(Date endTime) {
Date sTime = getStartTime();
if (sTime != null && endTime != null && sTime.after(endTime)) {
throw new IllegalArgumentException(
"End time cannot be before start time");
}
this.endTime = endTime;
}
public int getRepeatCount() {
return repeatCount;
}
public void setRepeatCount(int repeatCount) {
if (repeatCount < 0 && repeatCount != REPEAT_INDEFINITELY) {
throw new IllegalArgumentException(
"Repeat count must be >= 0, use the "
+ "constant REPEAT_INDEFINITELY for infinite.");
}
this.repeatCount = repeatCount;
}
public long getRepeatInterval() {
return repeatInterval;
}
public void setRepeatInterval(long repeatInterval) {
if (repeatInterval < 0) {
throw new IllegalArgumentException(
"Repeat interval must be >= 0");
}
this.repeatInterval = repeatInterval;
}
public int getTimesTriggered() {
return timesTriggered;
}
public void setTimesTriggered(int timesTriggered) {
this.timesTriggered = timesTriggered;
}
-------------------------------------------------------------------getter/setter 方法分割结束
*/
@Override
protected boolean validateMisfireInstruction(int misfireInstruction) {
if (misfireInstruction < MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY) {
return false;
}
if (misfireInstruction > MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_EXISTING_COUNT) {
return false;
}
return true;
}
@Override
public void updateAfterMisfire(Calendar cal) {
int instr = getMisfireInstruction();
if(instr == Trigger.MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY)
return;
if (instr == Trigger.MISFIRE_INSTRUCTION_SMART_POLICY) {
if (getRepeatCount() == 0) {
instr = MISFIRE_INSTRUCTION_FIRE_NOW;
} else if (getRepeatCount() == REPEAT_INDEFINITELY) {
instr = MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT;
} else {
// if (getRepeatCount() > 0)
instr = MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT;
}
} else if (instr == MISFIRE_INSTRUCTION_FIRE_NOW && getRepeatCount() != 0) {
instr = MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_REPEAT_COUNT;
}
if (instr == MISFIRE_INSTRUCTION_FIRE_NOW) {
setNextFireTime(new Date());
} else if (instr == MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_EXISTING_COUNT) {
Date newFireTime = getFireTimeAfter(new Date());
while (newFireTime != null && cal != null
&& !cal.isTimeIncluded(newFireTime.getTime())) {
newFireTime = getFireTimeAfter(newFireTime);
if(newFireTime == null)
break;
//avoid infinite loop
java.util.Calendar c = java.util.Calendar.getInstance();
c.setTime(newFireTime);
if (c.get(java.util.Calendar.YEAR) > YEAR_TO_GIVEUP_SCHEDULING_AT) {
newFireTime = null;
}
}
setNextFireTime(newFireTime);
} else if (instr == MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT) {
Date newFireTime = getFireTimeAfter(new Date());
while (newFireTime != null && cal != null
&& !cal.isTimeIncluded(newFireTime.getTime())) {
newFireTime = getFireTimeAfter(newFireTime);
if(newFireTime == null)
break;
//avoid infinite loop
java.util.Calendar c = java.util.Calendar.getInstance();
c.setTime(newFireTime);
if (c.get(java.util.Calendar.YEAR) > YEAR_TO_GIVEUP_SCHEDULING_AT) {
newFireTime = null;
}
}
if (newFireTime != null) {
int timesMissed = computeNumTimesFiredBetween(nextFireTime,
newFireTime);
setTimesTriggered(getTimesTriggered() + timesMissed);
}
setNextFireTime(newFireTime);
} else if (instr == MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT) {
Date newFireTime = new Date();
if (repeatCount != 0 && repeatCount != REPEAT_INDEFINITELY) {
setRepeatCount(getRepeatCount() - getTimesTriggered());
setTimesTriggered(0);
}
if (getEndTime() != null && getEndTime().before(newFireTime)) {
setNextFireTime(null); // We are past the end time
} else {
setStartTime(newFireTime);
setNextFireTime(newFireTime);
}
} else if (instr == MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_REPEAT_COUNT) {
Date newFireTime = new Date();
int timesMissed = computeNumTimesFiredBetween(nextFireTime,
newFireTime);
if (repeatCount != 0 && repeatCount != REPEAT_INDEFINITELY) {
int remainingCount = getRepeatCount()
- (getTimesTriggered() + timesMissed);
if (remainingCount <= 0) {
remainingCount = 0;
}
setRepeatCount(remainingCount);
setTimesTriggered(0);
}
if (getEndTime() != null && getEndTime().before(newFireTime)) {
setNextFireTime(null); // We are past the end time
} else {
setStartTime(newFireTime);
setNextFireTime(newFireTime);
}
}
}
/**
* 设置下次触发时间
* @param Calendar(日历)
*/
@Override
public void triggered(Calendar calendar) {
timesTriggered++;
previousFireTime = nextFireTime;
nextFireTime = getFireTimeAfter(nextFireTime);
while (nextFireTime != null && calendar != null
&& !calendar.isTimeIncluded(nextFireTime.getTime())) {
nextFireTime = getFireTimeAfter(nextFireTime);
if(nextFireTime == null)
break;
//avoid infinite loop
java.util.Calendar c = java.util.Calendar.getInstance();
c.setTime(nextFireTime);
if (c.get(java.util.Calendar.YEAR) > YEAR_TO_GIVEUP_SCHEDULING_AT) {
nextFireTime = null;
}
}
}
@Override
public void updateWithNewCalendar(Calendar calendar, long misfireThreshold)
{
nextFireTime = getFireTimeAfter(previousFireTime);
if (nextFireTime == null || calendar == null) {
return;
}
Date now = new Date();
while (nextFireTime != null && !calendar.isTimeIncluded(nextFireTime.getTime())) {
nextFireTime = getFireTimeAfter(nextFireTime);
if(nextFireTime == null)
break;
//avoid infinite loop
java.util.Calendar c = java.util.Calendar.getInstance();
c.setTime(nextFireTime);
if (c.get(java.util.Calendar.YEAR) > YEAR_TO_GIVEUP_SCHEDULING_AT) {
nextFireTime = null;
}
if(nextFireTime != null && nextFireTime.before(now)) {
long diff = now.getTime() - nextFireTime.getTime();
if(diff >= misfireThreshold) {
nextFireTime = getFireTimeAfter(nextFireTime);
}
}
}
}
/**
* 在传入日历的基础上,得到触发器第一次触发的时间
*/
@Override
public Date computeFirstFireTime(Calendar calendar) {
nextFireTime = getStartTime();
while (nextFireTime != null && calendar != null
&& !calendar.isTimeIncluded(nextFireTime.getTime())) {
nextFireTime = getFireTimeAfter(nextFireTime);
if(nextFireTime == null)
break;
//avoid infinite loop
java.util.Calendar c = java.util.Calendar.getInstance();
c.setTime(nextFireTime);
if (c.get(java.util.Calendar.YEAR) > YEAR_TO_GIVEUP_SCHEDULING_AT) {
return null;
}
}
return nextFireTime;
}
/** getter/setter分割线
--------------------------------------------------------------------
@Override
public Date getNextFireTime() {
return nextFireTime;
}
@Override
public Date getPreviousFireTime() {
return previousFireTime;
}
public void setNextFireTime(Date nextFireTime) {
this.nextFireTime = nextFireTime;
}
public void setPreviousFireTime(Date previousFireTime) {
this.previousFireTime = previousFireTime;
}
@Override
public Date getFireTimeAfter(Date afterTime) {
if (complete) {
return null;
}
if ((timesTriggered > repeatCount)
&& (repeatCount != REPEAT_INDEFINITELY)) {
return null;
}
if (afterTime == null) {
afterTime = new Date();
}
if (repeatCount == 0 && afterTime.compareTo(getStartTime()) >= 0) {
return null;
}
long startMillis = getStartTime().getTime();
long afterMillis = afterTime.getTime();
long endMillis = (getEndTime() == null) ? Long.MAX_VALUE : getEndTime()
.getTime();
if (endMillis <= afterMillis) {
return null;
}
if (afterMillis < startMillis) {
return new Date(startMillis);
}
long numberOfTimesExecuted = ((afterMillis - startMillis) / repeatInterval) + 1;
if ((numberOfTimesExecuted > repeatCount) &&
(repeatCount != REPEAT_INDEFINITELY)) {
return null;
}
Date time = new Date(startMillis + (numberOfTimesExecuted * repeatInterval));
if (endMillis <= time.getTime()) {
return null;
}
return time;
}
public Date getFireTimeBefore(Date end) {
if (end.getTime() < getStartTime().getTime()) {
return null;
}
int numFires = computeNumTimesFiredBetween(getStartTime(), end);
return new Date(getStartTime().getTime() + (numFires * repeatInterval));
}
public int computeNumTimesFiredBetween(Date start, Date end) {
if(repeatInterval < 1) {
return 0;
}
long time = end.getTime() - start.getTime();
return (int) (time / repeatInterval);
}
@Override
public Date getFinalFireTime() {
if (repeatCount == 0) {
return startTime;
}
if (repeatCount == REPEAT_INDEFINITELY) {
return (getEndTime() == null) ? null : getFireTimeBefore(getEndTime());
}
long lastTrigger = startTime.getTime() + (repeatCount * repeatInterval);
if ((getEndTime() == null) || (lastTrigger < getEndTime().getTime())) {
return new Date(lastTrigger);
} else {
return getFireTimeBefore(getEndTime());
}
}
--------------------------------------------------------------------
getter/setter 分隔结束
*/
/**
* 查看触发器是否还会再次触发
*/
@Override
public boolean mayFireAgain() {
return (getNextFireTime() != null);
}
/**
* 查看JobDetail设置的key-value是否提交到调度器中
* 如果未设置必需的属性(例如Name,Group,Class)则抛出
* IllegalStateException异常
*/
@Override
public void validate() throws SchedulerException {
super.validate();
if (repeatCount != 0 && repeatInterval < 1) {
throw new SchedulerException("Repeat Interval cannot be zero.");
}
}
public boolean hasAdditionalProperties() {
return false;
}
@Override
public ScheduleBuilder<SimpleTrigger> getScheduleBuilder() {
SimpleScheduleBuilder sb = SimpleScheduleBuilder.simpleSchedule()
.withIntervalInMilliseconds(getRepeatInterval())
.withRepeatCount(getRepeatCount());
switch(getMisfireInstruction()) {
case MISFIRE_INSTRUCTION_FIRE_NOW : sb.withMisfireHandlingInstructionFireNow();
break;
case MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_EXISTING_COUNT : sb.withMisfireHandlingInstructionNextWithExistingCount();
break;
case MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT : sb.withMisfireHandlingInstructionNextWithRemainingCount();
break;
case MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT : sb.withMisfireHandlingInstructionNowWithExistingCount();
break;
case MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_REPEAT_COUNT : sb.withMisfireHandlingInstructionNowWithRemainingCount();
break;
}
return sb;
}
}
6.CronTrigger(定时触发器)
使用CronTrigger,您可以指定号时间表,例如 “每周五中午” 或 “每个工作日和上午9:30”,甚至“每周一至周五上午9:00至10点之间每5分钟”和1月份的星期五“。
即使如此,和SimpleTrigger一样,CronTrigger有一个startTime,它指定何时生效,以及一个(可选的)endTime,用于指定何时停止计划。
关于Cron表达式的话没必要去记,可以使用Cron表达式 生成器 来进行生成
创建一个CronTriggerTest
import com.zgf.job.MailJob;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import org.quartz.impl.calendar.HolidayCalendar;
import org.quartz.impl.triggers.CronTriggerImpl;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import static org.quartz.CronScheduleBuilder.dailyAtHourAndMinute;
import static org.quartz.JobBuilder.newJob;
import static org.quartz.TriggerBuilder.newTrigger;
/**
* @Author 渣高帆
* @Date 2020/9/9 19:07
* @Version 1.0
* 作用:
**/
public class CronTriggerTest {
public static void main(String[] args) throws SchedulerException, ParseException {
//创建调度器工厂
SchedulerFactory sch = new StdSchedulerFactory();
Scheduler scheduler = sch.getScheduler();
scheduler.start();
//假期日历对象
HolidayCalendar cal = new HolidayCalendar();
//因为JDK8的Date(String)已经过时,这里使用日期格式化类
SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyy-MM-dd");
//通过Sparse
Date date=simpleDateFormat.parse("2020-09-15");
//添加假期时间,可以添加多个假期,通过cal.addExcludedDate(Date date)添加
cal.addExcludedDate(date);
//调度器添加日历
scheduler.addCalendar("myHolidays", cal, false,true);
// 定义工作并将其绑定到我们的DtlJob类
JobDetail job = newJob(MailJob.class)
// 名称“ myJob”,组“ group1”
.withIdentity("myJob", "group1")
//开始建造
.build();
//创建定时触发器实现类
CronTriggerImpl t = new CronTriggerImpl();
//调度器开始安排工作
scheduler.scheduleJob(job, t);
}
}
现在来看CronTriggerImpl
内部代码
public class CronTriggerImpl extends AbstractTrigger<CronTrigger> implements CronTrigger, CoreTrigger {
private static final long serialVersionUID = -8644953146451592766L;
private static final Logger LOGGER = LoggerFactory.getLogger(CronTriggerImpl.class);
protected static final int YEAR_TO_GIVEUP_SCHEDULING_AT = CronExpression.MAX_YEAR;
//私有属性,已经封装好了getter/setter方法
//Cron表达式
private CronExpression cronEx = null;
//开始时间
private Date startTime = null;
//结束时间
private Date endTime = null;
//下一次执行时间
private Date nextFireTime = null;
//上一次执行时间
private Date previousFireTime = null;
//时区
private transient TimeZone timeZone = null;
/**
* 无参构造开始时间也将设置为当前时间,时区将设置为系统的默认时区。
*/
public CronTriggerImpl() {
super();
setStartTime(new Date());
setTimeZone(TimeZone.getDefault());
}
/**
* 开始时间也将设置为当前时间,时区将设置为系统的默认时区。
* @param name 设置触发器名字
*/
@Deprecated
public CronTriggerImpl(String name) {
this(name, null);
}
/**
* 开始时间也将设置为当前时间,时区将设置为系统的默认时区。
* @param name 设置触发器名字
* @param group 设置触发器分组
*/
@Deprecated
public CronTriggerImpl(String name, String group) {
super(name, group);
setStartTime(new Date());
setTimeZone(TimeZone.getDefault());
}
/**
* 开始时间也将设置为当前时间,时区将设置为系统的默认时区。
* @param name 设置触发器名字
* @param group 设置触发器分组
* @param cronExpression 添加cron表达式
*/
@Deprecated
public CronTriggerImpl(String name, String group, String cronExpression)
throws ParseException {
super(name, group);
setCronExpression(cronExpression);
setStartTime(new Date());
setTimeZone(TimeZone.getDefault());
}
/**
* 开始时间也将设置为当前时间,时区将设置为系统的默认时区。
* @param name 设置触发器名字
* @param group 设置触发器分组
* @param jobName 关联一个Job
* @param cronExpression 添加cron表达式
*/
@Deprecated
public CronTriggerImpl(String name, String group, String jobName,
String jobGroup) {
super(name, group, jobName, jobGroup);
setStartTime(new Date());
setTimeZone(TimeZone.getDefault());
}
/**
* 开始时间也将设置为当前时间,时区将设置为系统的默认时区。
* @param name 设置触发器名字
* @param group 设置触发器分组
* @param jobName 关联一个Job
* @param jobGroup Job属于组
* @param cronExpression 添加cron表达式
*/
@Deprecated
public CronTriggerImpl(String name, String group, String jobName,
String jobGroup, String cronExpression) throws ParseException {
this(name, group, jobName, jobGroup, null, null, cronExpression,
TimeZone.getDefault());
}
/**
* 开始时间也将设置为当前时间,时区将设置为系统的默认时区。
* @param name 设置触发器名字
* @param group 设置触发器分组
* @param jobName 关联一个Job
* @param jobGroup Job属于组
* @param cronExpression 添加cron表达式
* @param TimeZone 添加时区
*/
@Deprecated
public CronTriggerImpl(String name, String group, String jobName,
String jobGroup, String cronExpression, TimeZone timeZone)
throws ParseException {
this(name, group, jobName, jobGroup, null, null, cronExpression,
timeZone);
}
/**
* 开始时间也将设置为当前时间,时区将设置为系统的默认时区。
* @param name 设置触发器名字
* @param group 设置触发器分组
* @param jobName 关联一个Job
* @param jobGroup Job属于组
* @param cronExpression 添加cron表达式
* @param startTime 开始时间
* @param endTime 结束时间
*/
@Deprecated
public CronTriggerImpl(String name, String group, String jobName,
String jobGroup, Date startTime, Date endTime, String cronExpression)
throws ParseException {
super(name, group, jobName, jobGroup);
setCronExpression(cronExpression);
if (startTime == null) {
startTime = new Date();
}
setStartTime(startTime);
if (endTime != null) {
setEndTime(endTime);
}
setTimeZone(TimeZone.getDefault());
}
/**
* 开始时间也将设置为当前时间,时区将设置为系统的默认时区。
* @param name 设置触发器名字
* @param group 设置触发器分组
* @param jobName 关联一个Job
* @param jobGroup Job属于组
* @param cronExpression 添加cron表达式
* @param startTime 开始时间
* @param endTime 结束时间
* @param TimeZone 添加时区
*/
@Deprecated
public CronTriggerImpl(String name, String group, String jobName,
String jobGroup, Date startTime, Date endTime,
String cronExpression, TimeZone timeZone) throws ParseException {
super(name, group, jobName, jobGroup);
setCronExpression(cronExpression);
if (startTime == null) {
startTime = new Date();
}
setStartTime(startTime);
if (endTime != null) {
setEndTime(endTime);
}
if (timeZone == null) {
setTimeZone(TimeZone.getDefault());
} else {
setTimeZone(timeZone);
}
}
/**
*克隆一个定时触发器
*/
@Override
public Object clone() {
CronTriggerImpl copy = (CronTriggerImpl) super.clone();
if (cronEx != null) {
copy.setCronExpression(new CronExpression(cronEx));
}
return copy;
}
/**
* getter/setter分割线
---------------------------------------------------------------
public void setCronExpression(String cronExpression) throws ParseException {
TimeZone origTz = getTimeZone();
this.cronEx = new CronExpression(cronExpression);
this.cronEx.setTimeZone(origTz);
}
public String getCronExpression() {
return cronEx == null ? null : cronEx.getCronExpression();
}
public void setCronExpression(CronExpression cronExpression) {
this.cronEx = cronExpression;
this.timeZone = cronExpression.getTimeZone();
}
@Override
public Date getStartTime() {
return this.startTime;
}
@Override
public void setStartTime(Date startTime) {
if (startTime == null) {
throw new IllegalArgumentException("Start time cannot be null");
}
Date eTime = getEndTime();
if (eTime != null && eTime.before(startTime)) {
throw new IllegalArgumentException(
"End time cannot be before start time");
}
Calendar cl = Calendar.getInstance();
cl.setTime(startTime);
cl.set(Calendar.MILLISECOND, 0);
this.startTime = cl.getTime();
}
@Override
public Date getEndTime() {
return this.endTime;
}
@Override
public void setEndTime(Date endTime) {
Date sTime = getStartTime();
if (sTime != null && endTime != null && sTime.after(endTime)) {
throw new IllegalArgumentException(
"End time cannot be before start time");
}
this.endTime = endTime;
}
@Override
public Date getNextFireTime() {
return this.nextFireTime;
}
@Override
public Date getPreviousFireTime() {
return this.previousFireTime;
}
public void setNextFireTime(Date nextFireTime) {
this.nextFireTime = nextFireTime;
}
public void setPreviousFireTime(Date previousFireTime) {
this.previousFireTime = previousFireTime;
}
public TimeZone getTimeZone() {
if(cronEx != null) {
return cronEx.getTimeZone();
}
if (timeZone == null) {
timeZone = TimeZone.getDefault();
}
return timeZone;
}
public void setTimeZone(TimeZone timeZone) {
if(cronEx != null) {
cronEx.setTimeZone(timeZone);
}
this.timeZone = timeZone;
}
@Override
public Date getFireTimeAfter(Date afterTime) {
if (afterTime == null) {
afterTime = new Date();
}
if (getStartTime().after(afterTime)) {
afterTime = new Date(getStartTime().getTime() - 1000l);
}
if (getEndTime() != null && (afterTime.compareTo(getEndTime()) >= 0)) {
return null;
}
Date pot = getTimeAfter(afterTime);
if (getEndTime() != null && pot != null && pot.after(getEndTime())) {
return null;
}
return pot;
}
@Override
public Date getFinalFireTime() {
Date resultTime;
if (getEndTime() != null) {
resultTime = getTimeBefore(new Date(getEndTime().getTime() + 1000l));
} else {
resultTime = (cronEx == null) ? null : cronEx.getFinalFireTime();
}
if ((resultTime != null) && (getStartTime() != null) && (resultTime.before(getStartTime()))) {
return null;
}
return resultTime;
}
--------------------------------------------------------------------
getter/setter 分割结束
*/
/**
* 是否还会触发
*/
@Override
public boolean mayFireAgain() {
return (getNextFireTime() != null);
}
@Override
protected boolean validateMisfireInstruction(int misfireInstruction) {
return misfireInstruction >= MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY && misfireInstruction <= MISFIRE_INSTRUCTION_DO_NOTHING;
}
@Override
public void updateAfterMisfire(org.quartz.Calendar cal) {
int instr = getMisfireInstruction();
if(instr == Trigger.MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY)
return;
if (instr == MISFIRE_INSTRUCTION_SMART_POLICY) {
instr = MISFIRE_INSTRUCTION_FIRE_ONCE_NOW;
}
if (instr == MISFIRE_INSTRUCTION_DO_NOTHING) {
Date newFireTime = getFireTimeAfter(new Date());
while (newFireTime != null && cal != null
&& !cal.isTimeIncluded(newFireTime.getTime())) {
newFireTime = getFireTimeAfter(newFireTime);
}
setNextFireTime(newFireTime);
} else if (instr == MISFIRE_INSTRUCTION_FIRE_ONCE_NOW) {
setNextFireTime(new Date());
}
}
/**
* 测试日历是否添加成功
* @param test 日历
*/
public boolean willFireOn(Calendar test) {
return willFireOn(test, false);
}
/* 确定给定Calendar实例的日期和时间(可选)是否在此触发器的计划启动时间上。 * @param test 测试要比较的日期
* @param dayOnly如果设置为true,则该方法将仅确定触发器是否将在给定 * Calendar表示的日期内触发(小时,分钟和秒将被忽略)。
*/
public boolean willFireOn(Calendar test, boolean dayOnly) {
test = (Calendar) test.clone();
test.set(Calendar.MILLISECOND, 0); // don't compare millis.
if(dayOnly) {
test.set(Calendar.HOUR_OF_DAY, 0);
test.set(Calendar.MINUTE, 0);
test.set(Calendar.SECOND, 0);
}
Date testTime = test.getTime();
Date fta = getFireTimeAfter(new Date(test.getTime().getTime() - 1000));
if(fta == null)
return false;
Calendar p = Calendar.getInstance(test.getTimeZone());
p.setTime(fta);
int year = p.get(Calendar.YEAR);
int month = p.get(Calendar.MONTH);
int day = p.get(Calendar.DATE);
if(dayOnly) {
return (year == test.get(Calendar.YEAR)
&& month == test.get(Calendar.MONTH)
&& day == test.get(Calendar.DATE));
}
while(fta.before(testTime)) {
fta = getFireTimeAfter(fta);
}
return fta.equals(testTime);
}
@Override
public void triggered(org.quartz.Calendar calendar) {
previousFireTime = nextFireTime;
nextFireTime = getFireTimeAfter(nextFireTime);
while (nextFireTime != null && calendar != null
&& !calendar.isTimeIncluded(nextFireTime.getTime())) {
nextFireTime = getFireTimeAfter(nextFireTime);
}
}
@Override
public void updateWithNewCalendar(org.quartz.Calendar calendar, long misfireThreshold)
{
nextFireTime = getFireTimeAfter(previousFireTime);
if (nextFireTime == null || calendar == null) {
return;
}
Date now = new Date();
while (nextFireTime != null && !calendar.isTimeIncluded(nextFireTime.getTime())) {
nextFireTime = getFireTimeAfter(nextFireTime);
if(nextFireTime == null)
break;
java.util.Calendar c = new java.util.GregorianCalendar();
c.setTime(nextFireTime);
if (c.get(java.util.Calendar.YEAR) > YEAR_TO_GIVEUP_SCHEDULING_AT) {
nextFireTime = null;
}
if(nextFireTime != null && nextFireTime.before(now)) {
long diff = now.getTime() - nextFireTime.getTime();
if(diff >= misfireThreshold) {
nextFireTime = getFireTimeAfter(nextFireTime);
}
}
}
}
/**
* 计算触发器首次触发时间
* @param calendar 传入日历计算
*/
@Override
public Date computeFirstFireTime(org.quartz.Calendar calendar) {
nextFireTime = getFireTimeAfter(new Date(getStartTime().getTime() - 1000l));
while (nextFireTime != null && calendar != null
&& !calendar.isTimeIncluded(nextFireTime.getTime())) {
nextFireTime = getFireTimeAfter(nextFireTime);
}
return nextFireTime;
}
/*
* 获取表达式摘要
*/
public String getExpressionSummary() {
return cronEx == null ? null : cronEx.getExpressionSummary();
}
/**
* 是否具有其他属性
*/
public boolean hasAdditionalProperties() {
return false;
}
/**
* 获取时间表生成器
*/
@Override
public ScheduleBuilder<CronTrigger> getScheduleBuilder() {
CronScheduleBuilder cb = CronScheduleBuilder.cronSchedule(getCronExpression())
.inTimeZone(getTimeZone());
int misfireInstruction = getMisfireInstruction();
switch(misfireInstruction) {
case MISFIRE_INSTRUCTION_SMART_POLICY:
break;
case MISFIRE_INSTRUCTION_DO_NOTHING:
cb.withMisfireHandlingInstructionDoNothing();
break;
case MISFIRE_INSTRUCTION_FIRE_ONCE_NOW:
cb.withMisfireHandlingInstructionFireAndProceed();
break;
case MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY:
cb.withMisfireHandlingInstructionIgnoreMisfires();
break;
default:
LOGGER.warn("Unrecognized misfire policy {}. Derived builder will use the default cron trigger behavior (MISFIRE_INSTRUCTION_FIRE_ONCE_NOW)", misfireInstruction);
}
return cb;
}
/**
* 得到Cron表达式触发完的时间
*/
protected Date getTimeAfter(Date afterTime) {
return (cronEx == null) ? null : cronEx.getTimeAfter(afterTime);
}
/**
* 得到Cron表达式触发的时间
*/
protected Date getTimeBefore(Date eTime) {
return (cronEx == null) ? null : cronEx.getTimeBefore(eTime);
}
}
7.CalendarIntervalTrigger(日历间隔触发器)
目前已经简单了解了两种触发器,“简单触发器”和“计时触发器”,下面这两个触发器不常用但也可以了解一下
在Quartz中不仅有日历对象也有日历触发器,触发器的创建方式都是一样的这里就直接省略了,下面我们进入它的实现类内部
public class CalendarIntervalTriggerImpl extends AbstractTrigger<CalendarIntervalTrigger> implements CalendarIntervalTrigger, CoreTrigger {
private static final long serialVersionUID = -2635982274232850343L;
private static final int YEAR_TO_GIVEUP_SCHEDULING_AT = java.util.Calendar.getInstance().get(java.util.Calendar.YEAR) + 100;
/**
* 和前两个触发器一样这里直接在私有属性上介绍,不再去看getter/setter方法
*/
//开始时间
private Date startTime = null;
//结束时间
private Date endTime = null;
//下一次触发时间
private Date nextFireTime = null;
//上次触发时间
private Date previousFireTime = null;
//间隔时间,必须为正数,具体间隔单位根据IntervalUnit来设定
private int repeatInterval = 0;
//间隔单位,默认单位为日,具体去看IntervalUnit内部
private IntervalUnit repeatIntervalUnit = IntervalUnit.DAY;
//时区
private TimeZone timeZone;
//是否开启夏时制
private boolean preserveHourOfDayAcrossDaylightSavings = false;
//如果设置的小时不存在是否跳过这一天
private boolean skipDayIfHourDoesNotExist = false;
//已经触发次数
private int timesTriggered = 0;
//触发器是否触发完成
private boolean complete = false;
/** 构造器参照上面两个构造器
--------------------------------------------------------------------
public CalendarIntervalTriggerImpl() {
super();
}
public CalendarIntervalTriggerImpl(String name, IntervalUnit intervalUnit, int repeatInterval) {
this(name, null, intervalUnit, repeatInterval);
}
public CalendarIntervalTriggerImpl(String name, String group, IntervalUnit intervalUnit,
int repeatInterval) {
this(name, group, new Date(), null, intervalUnit, repeatInterval);
}
public CalendarIntervalTriggerImpl(String name, Date startTime,
Date endTime, IntervalUnit intervalUnit, int repeatInterval) {
this(name, null, startTime, endTime, intervalUnit, repeatInterval);
}
public CalendarIntervalTriggerImpl(String name, String group, Date startTime,
Date endTime, IntervalUnit intervalUnit, int repeatInterval) {
super(name, group);
setStartTime(startTime);
setEndTime(endTime);
setRepeatIntervalUnit(intervalUnit);
setRepeatInterval(repeatInterval);
}
public CalendarIntervalTriggerImpl(String name, String group, String jobName,
String jobGroup, Date startTime, Date endTime,
IntervalUnit intervalUnit, int repeatInterval) {
super(name, group, jobName, jobGroup);
setStartTime(startTime);
setEndTime(endTime);
setRepeatIntervalUnit(intervalUnit);
setRepeatInterval(repeatInterval);
}
--------------------------------------------------------------------
*/
/** getter/setter 分割
--------------------------------------------------------------------
@Override
public Date getStartTime() {
if(startTime == null)
startTime = new Date();
return startTime;
}
@Override
public void setStartTime(Date startTime) {
if (startTime == null) {
throw new IllegalArgumentException("Start time cannot be null");
}
Date eTime = getEndTime();
if (eTime != null && eTime.before(startTime)) {
throw new IllegalArgumentException(
"End time cannot be before start time");
}
this.startTime = startTime;
}
@Override
public Date getEndTime() {
return endTime;
}
@Override
public void setEndTime(Date endTime) {
Date sTime = getStartTime();
if (sTime != null && endTime != null && sTime.after(endTime)) {
throw new IllegalArgumentException(
"End time cannot be before start time");
}
this.endTime = endTime;
}
public IntervalUnit getRepeatIntervalUnit() {
return repeatIntervalUnit;
}
public void setRepeatIntervalUnit(IntervalUnit intervalUnit) {
this.repeatIntervalUnit = intervalUnit;
}
public int getRepeatInterval() {
return repeatInterval;
}
public void setRepeatInterval( int repeatInterval) {
if (repeatInterval < 0) {
throw new IllegalArgumentException(
"Repeat interval must be >= 1");
}
this.repeatInterval = repeatInterval;
}
public TimeZone getTimeZone() {
if (timeZone == null) {
timeZone = TimeZone.getDefault();
}
return timeZone;
}
public void setTimeZone(TimeZone timeZone) {
this.timeZone = timeZone;
}
public boolean isPreserveHourOfDayAcrossDaylightSavings() {
return preserveHourOfDayAcrossDaylightSavings;
}
public void setPreserveHourOfDayAcrossDaylightSavings(boolean preserveHourOfDayAcrossDaylightSavings) {
this.preserveHourOfDayAcrossDaylightSavings = preserveHourOfDayAcrossDaylightSavings;
}
public boolean isSkipDayIfHourDoesNotExist() {
return skipDayIfHourDoesNotExist;
}
public void setSkipDayIfHourDoesNotExist(boolean skipDayIfHourDoesNotExist) {
this.skipDayIfHourDoesNotExist = skipDayIfHourDoesNotExist;
}
public int getTimesTriggered() {
return timesTriggered;
}
public void setTimesTriggered(int timesTriggered) {
this.timesTriggered = timesTriggered;
}
--------------------------------------------------------------------
*/
@Override
protected boolean validateMisfireInstruction(int misfireInstruction) {
if (misfireInstruction < MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY) {
return false;
}
return misfireInstruction <= MISFIRE_INSTRUCTION_DO_NOTHING;
}
@Override
public void updateAfterMisfire(org.quartz.Calendar cal) {
int instr = getMisfireInstruction();
if(instr == Trigger.MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY)
return;
if (instr == MISFIRE_INSTRUCTION_SMART_POLICY) {
instr = MISFIRE_INSTRUCTION_FIRE_ONCE_NOW;
}
if (instr == MISFIRE_INSTRUCTION_DO_NOTHING) {
Date newFireTime = getFireTimeAfter(new Date());
while (newFireTime != null && cal != null
&& !cal.isTimeIncluded(newFireTime.getTime())) {
newFireTime = getFireTimeAfter(newFireTime);
}
setNextFireTime(newFireTime);
} else if (instr == MISFIRE_INSTRUCTION_FIRE_ONCE_NOW) {
setNextFireTime(new Date());
}
}
/**
* 通过日历进行二次触发
*/
@Override
public void triggered(org.quartz.Calendar calendar) {
timesTriggered++;
previousFireTime = nextFireTime;
nextFireTime = getFireTimeAfter(nextFireTime);
while (nextFireTime != null && calendar != null
&& !calendar.isTimeIncluded(nextFireTime.getTime())) {
nextFireTime = getFireTimeAfter(nextFireTime);
if(nextFireTime == null)
break;
java.util.Calendar c = java.util.Calendar.getInstance();
c.setTime(nextFireTime);
if (c.get(java.util.Calendar.YEAR) > YEAR_TO_GIVEUP_SCHEDULING_AT) {
nextFireTime = null;
}
}
}
@Override
public void updateWithNewCalendar(org.quartz.Calendar calendar, long misfireThreshold)
{
nextFireTime = getFireTimeAfter(previousFireTime);
if (nextFireTime == null || calendar == null) {
return;
}
Date now = new Date();
while (nextFireTime != null && !calendar.isTimeIncluded(nextFireTime.getTime())) {
nextFireTime = getFireTimeAfter(nextFireTime);
if(nextFireTime == null)
break;
java.util.Calendar c = java.util.Calendar.getInstance();
c.setTime(nextFireTime);
if (c.get(java.util.Calendar.YEAR) > YEAR_TO_GIVEUP_SCHEDULING_AT) {
nextFireTime = null;
}
if(nextFireTime != null && nextFireTime.before(now)) {
long diff = now.getTime() - nextFireTime.getTime();
if(diff >= misfireThreshold) {
nextFireTime = getFireTimeAfter(nextFireTime);
}
}
}
}
/**
* 首次触发时间
*/
@Override
public Date computeFirstFireTime(org.quartz.Calendar calendar) {
nextFireTime = getStartTime();
while (nextFireTime != null && calendar != null
&& !calendar.isTimeIncluded(nextFireTime.getTime())) {
nextFireTime = getFireTimeAfter(nextFireTime);
if(nextFireTime == null)
break;
//avoid infinite loop
java.util.Calendar c = java.util.Calendar.getInstance();
c.setTime(nextFireTime);
if (c.get(java.util.Calendar.YEAR) > YEAR_TO_GIVEUP_SCHEDULING_AT) {
return null;
}
}
return nextFireTime;
}
/** getter/setter分割
--------------------------------------------------------------------
@Override
public Date getNextFireTime() {
return nextFireTime;
}
@Override
public Date getPreviousFireTime() {
return previousFireTime;
}
public void setNextFireTime(Date nextFireTime) {
this.nextFireTime = nextFireTime;
}
public void setPreviousFireTime(Date previousFireTime) {
this.previousFireTime = previousFireTime;
}
@Override
public Date getFireTimeAfter(Date afterTime) {
return getFireTimeAfter(afterTime, false);
}
protected Date getFireTimeAfter(Date afterTime, boolean ignoreEndTime) {
if (complete) {
return null;
}
if (afterTime == null) {
afterTime = new Date();
}
long startMillis = getStartTime().getTime();
long afterMillis = afterTime.getTime();
long endMillis = (getEndTime() == null) ? Long.MAX_VALUE : getEndTime()
.getTime();
if (!ignoreEndTime && (endMillis <= afterMillis)) {
return null;
}
if (afterMillis < startMillis) {
return new Date(startMillis);
}
long secondsAfterStart = 1 + (afterMillis - startMillis) / 1000L;
Date time = null;
long repeatLong = getRepeatInterval();
Calendar aTime = Calendar.getInstance();
aTime.setTime(afterTime);
Calendar sTime = Calendar.getInstance();
if(timeZone != null)
sTime.setTimeZone(timeZone);
sTime.setTime(getStartTime());
sTime.setLenient(true);
if(getRepeatIntervalUnit().equals(IntervalUnit.SECOND)) {
long jumpCount = secondsAfterStart / repeatLong;
if(secondsAfterStart % repeatLong != 0)
jumpCount++;
sTime.add(Calendar.SECOND, getRepeatInterval() * (int)jumpCount);
time = sTime.getTime();
}
else if(getRepeatIntervalUnit().equals(IntervalUnit.MINUTE)) {
long jumpCount = secondsAfterStart / (repeatLong * 60L);
if(secondsAfterStart % (repeatLong * 60L) != 0)
jumpCount++;
sTime.add(Calendar.MINUTE, getRepeatInterval() * (int)jumpCount);
time = sTime.getTime();
}
else if(getRepeatIntervalUnit().equals(IntervalUnit.HOUR)) {
long jumpCount = secondsAfterStart / (repeatLong * 60L * 60L);
if(secondsAfterStart % (repeatLong * 60L * 60L) != 0)
jumpCount++;
sTime.add(Calendar.HOUR_OF_DAY, getRepeatInterval() * (int)jumpCount);
time = sTime.getTime();
}
else { // intervals a day or greater ...
int initialHourOfDay = sTime.get(Calendar.HOUR_OF_DAY);
if(getRepeatIntervalUnit().equals(IntervalUnit.DAY)) {
sTime.setLenient(true);
long jumpCount = secondsAfterStart / (repeatLong * 24L * 60L * 60L);
if(jumpCount > 20) {
if(jumpCount < 50)
jumpCount = (long) (jumpCount * 0.80);
else if(jumpCount < 500)
jumpCount = (long) (jumpCount * 0.90);
else
jumpCount = (long) (jumpCount * 0.95);
sTime.add(java.util.Calendar.DAY_OF_YEAR, (int) (getRepeatInterval() * jumpCount));
}
while(!sTime.getTime().after(afterTime) &&
(sTime.get(java.util.Calendar.YEAR) < YEAR_TO_GIVEUP_SCHEDULING_AT)) {
sTime.add(java.util.Calendar.DAY_OF_YEAR, getRepeatInterval());
}
while(daylightSavingHourShiftOccurredAndAdvanceNeeded(sTime, initialHourOfDay, afterTime) &&
(sTime.get(java.util.Calendar.YEAR) < YEAR_TO_GIVEUP_SCHEDULING_AT)) {
sTime.add(java.util.Calendar.DAY_OF_YEAR, getRepeatInterval());
}
time = sTime.getTime();
}
else if(getRepeatIntervalUnit().equals(IntervalUnit.WEEK)) {
sTime.setLenient(true);
long jumpCount = secondsAfterStart / (repeatLong * 7L * 24L * 60L * 60L);
if(jumpCount > 20) {
if(jumpCount < 50)
jumpCount = (long) (jumpCount * 0.80);
else if(jumpCount < 500)
jumpCount = (long) (jumpCount * 0.90);
else
jumpCount = (long) (jumpCount * 0.95);
sTime.add(java.util.Calendar.WEEK_OF_YEAR, (int) (getRepeatInterval() * jumpCount));
}
while(!sTime.getTime().after(afterTime) &&
(sTime.get(java.util.Calendar.YEAR) < YEAR_TO_GIVEUP_SCHEDULING_AT)) {
sTime.add(java.util.Calendar.WEEK_OF_YEAR, getRepeatInterval());
}
while(daylightSavingHourShiftOccurredAndAdvanceNeeded(sTime, initialHourOfDay, afterTime) &&
(sTime.get(java.util.Calendar.YEAR) < YEAR_TO_GIVEUP_SCHEDULING_AT)) {
sTime.add(java.util.Calendar.WEEK_OF_YEAR, getRepeatInterval());
}
time = sTime.getTime();
}
else if(getRepeatIntervalUnit().equals(IntervalUnit.MONTH)) {
sTime.setLenient(true);
while(!sTime.getTime().after(afterTime) &&
(sTime.get(java.util.Calendar.YEAR) < YEAR_TO_GIVEUP_SCHEDULING_AT)) {
sTime.add(java.util.Calendar.MONTH, getRepeatInterval());
}
while(daylightSavingHourShiftOccurredAndAdvanceNeeded(sTime, initialHourOfDay, afterTime) &&
(sTime.get(java.util.Calendar.YEAR) < YEAR_TO_GIVEUP_SCHEDULING_AT)) {
sTime.add(java.util.Calendar.MONTH, getRepeatInterval());
}
time = sTime.getTime();
}
else if(getRepeatIntervalUnit().equals(IntervalUnit.YEAR)) {
while(!sTime.getTime().after(afterTime) &&
(sTime.get(java.util.Calendar.YEAR) < YEAR_TO_GIVEUP_SCHEDULING_AT)) {
sTime.add(java.util.Calendar.YEAR, getRepeatInterval());
}
while(daylightSavingHourShiftOccurredAndAdvanceNeeded(sTime, initialHourOfDay, afterTime) &&
(sTime.get(java.util.Calendar.YEAR) < YEAR_TO_GIVEUP_SCHEDULING_AT)) {
sTime.add(java.util.Calendar.YEAR, getRepeatInterval());
}
time = sTime.getTime();
}
}
if (!ignoreEndTime && (endMillis <= time.getTime())) {
return null;
}
return time;
}
private boolean daylightSavingHourShiftOccurredAndAdvanceNeeded(Calendar newTime, int initialHourOfDay, Date afterTime) {
if(isPreserveHourOfDayAcrossDaylightSavings() && newTime.get(Calendar.HOUR_OF_DAY) != initialHourOfDay) {
newTime.set(Calendar.HOUR_OF_DAY, initialHourOfDay);
if (newTime.get(Calendar.HOUR_OF_DAY) != initialHourOfDay) {
return isSkipDayIfHourDoesNotExist();
} else {
return !newTime.getTime().after(afterTime);
}
}
return false;
}
@Override
public Date getFinalFireTime() {
if (complete || getEndTime() == null) {
return null;
}
Date fTime = new Date(getEndTime().getTime() - 1000L);
fTime = getFireTimeAfter(fTime, true);
if(fTime.equals(getEndTime()))
return fTime;
Calendar lTime = Calendar.getInstance();
if(timeZone != null)
lTime.setTimeZone(timeZone);
lTime.setTime(fTime);
lTime.setLenient(true);
if(getRepeatIntervalUnit().equals(IntervalUnit.SECOND)) {
lTime.add(java.util.Calendar.SECOND, -1 * getRepeatInterval());
}
else if(getRepeatIntervalUnit().equals(IntervalUnit.MINUTE)) {
lTime.add(java.util.Calendar.MINUTE, -1 * getRepeatInterval());
}
else if(getRepeatIntervalUnit().equals(IntervalUnit.HOUR)) {
lTime.add(java.util.Calendar.HOUR_OF_DAY, -1 * getRepeatInterval());
}
else if(getRepeatIntervalUnit().equals(IntervalUnit.DAY)) {
lTime.add(java.util.Calendar.DAY_OF_YEAR, -1 * getRepeatInterval());
}
else if(getRepeatIntervalUnit().equals(IntervalUnit.WEEK)) {
lTime.add(java.util.Calendar.WEEK_OF_YEAR, -1 * getRepeatInterval());
}
else if(getRepeatIntervalUnit().equals(IntervalUnit.MONTH)) {
lTime.add(java.util.Calendar.MONTH, -1 * getRepeatInterval());
}
else if(getRepeatIntervalUnit().equals(IntervalUnit.YEAR)) {
lTime.add(java.util.Calendar.YEAR, -1 * getRepeatInterval());
}
return lTime.getTime();
}
--------------------------------------------------------------------
*/
/**
* 触发器是否还会触发
*/
@Override
public boolean mayFireAgain() {
return (getNextFireTime() != null);
}
/**
* 验证是否获取到JobDetail中的属性,未获取到抛异常
*/
@Override
public void validate() throws SchedulerException {
super.validate();
if (repeatInterval < 1) {
throw new SchedulerException("Repeat Interval cannot be zero.");
}
}
/**
* 获取一个时间表生成器
*/
@Override
public ScheduleBuilder<CalendarIntervalTrigger> getScheduleBuilder() {
CalendarIntervalScheduleBuilder cb = CalendarIntervalScheduleBuilder.calendarIntervalSchedule()
.withInterval(getRepeatInterval(), getRepeatIntervalUnit());
switch(getMisfireInstruction()) {
case MISFIRE_INSTRUCTION_DO_NOTHING : cb.withMisfireHandlingInstructionDoNothing();
break;
case MISFIRE_INSTRUCTION_FIRE_ONCE_NOW : cb.withMisfireHandlingInstructionFireAndProceed();
break;
}
return cb;
}
/**
* 是否具有其他属性
*/
public boolean hasAdditionalProperties() {
return false;
}
}
8.DailyTimeIntervalTrigger(每日时间间隔触发器)
四个触发器内部实现基本上都是相似的,关于DailyTimeIntervalTriggerImpl这里只介绍内部属性
//开始时间
private Date startTime = null;
//结束时间
private Date endTime = null;
//下一次触发时间
private Date nextFireTime = null;
//上次触发时间
private Date previousFireTime = null;
//重复次数,默认-1表示无限次,除非自己设置次数或设置停止时间才会停止
private int repeatCount = REPEAT_INDEFINITELY;
//触发时间间隔,以分钟为单位,默认间隔一分钟
private int repeatInterval = 1;
//间隔单位,分钟
private IntervalUnit repeatIntervalUnit = IntervalUnit.MINUTE;
//星期几会触发
private Set<Integer> daysOfWeek;
//创建一个TimeOfDay对象,可以通过TimeOfDay设定开始时间,创建时需传入
//小时,分钟,秒(可选)
private TimeOfDay startTimeOfDay;
//创建一个TimeOfDay对象,可以通过TimeOfDay设定结束时间,创建时需传入
//小时,分钟,秒(可选)
private TimeOfDay endTimeOfDay;
//已经触发的次数
private int timesTriggered = 0;
//触发器是否执行完毕
private boolean complete = false;
8.Trigger类中的newTrigger()方法
想对于创建四种类型的触发器实例外Trigger
中的newTrigger()
方法无疑是更实用更方便的选择了,下面带你们去看一下newTrigger()
的简便之处。
长白山压弯王要压弯了,系好安全带,上车
//当调用newTrigger()方法时创建一个TriggerBuilder<Trigger>泛型类
Trigger trigger = newTrigger().build();
//看一下newTrigger()方法内部,返回一个TriggerBuilder<Trigger>对象
public static TriggerBuilder<Trigger> newTrigger() {
return new TriggerBuilder<Trigger>();
}
/*
那么来看一下为什么需要使用build()方法,build()为TriggerBuilder<Trigger>
的内部方法,因为TriggerBuilder<Trigger>并没有实现Trigger接口,所有不使用build()方法的话会产生类型不匹配而导致编译不通过,build()方法的返回值刚好就是TriggerBuilder<Trigger>泛型类传入的Trigger对象
*/
public T build() {
if(scheduleBuilder == null)
scheduleBuilder = SimpleScheduleBuilder.simpleSchedule();
MutableTrigger trig = scheduleBuilder.build();
//关于这些方法下面慢慢讲解
trig.setCalendarName(calendarName);
trig.setDescription(description);
trig.setStartTime(startTime);
trig.setEndTime(endTime);
if(key == null)
key = new TriggerKey(Key.createUniqueName(null), null);
trig.setKey(key);
if(jobKey != null)
trig.setJobKey(jobKey);
trig.setPriority(priority);
if(!jobDataMap.isEmpty())
trig.setJobDataMap(jobDataMap);
return (T) trig;
}
下面我们进入TriggerBuilder<T extends Trigger>
的内部去看看它为什么比直接使用那四个触发更方便;
public class TriggerBuilder<T extends Trigger> {
//触发器键对象,通过它可以设置触发器名字和触发器分组等等......
private TriggerKey key;
//触发器的描述
private String description;
//开始的时间,默认当前时间
private Date startTime = new Date();
//结束的时间
private Date endTime;
//触发器优先级越大优先级越高,整数,默认值为5
private int priority = Trigger.DEFAULT_PRIORITY;
//日历名称
private String calendarName;
//Job(作业)键对象,可以通过名称或分组来设置使用某个Job
private JobKey jobKey;
//JobDataMap对象,关于JobDataMap对象的使用可以看前面的介绍
private JobDataMap jobDataMap = new JobDataMap();
//这个就不用说了,当前类
private ScheduleBuilder<?> scheduleBuilder = null;
private TriggerBuilder() {
}
/**
* 创建一个TriggerBuilder<Trigger>对象
*/
public static TriggerBuilder<Trigger> newTrigger() {
return new TriggerBuilder<Trigger>();
}
/**
* 返回Trigger对象
*/
@SuppressWarnings("unchecked")
public T build() {
if(scheduleBuilder == null)
scheduleBuilder = SimpleScheduleBuilder.simpleSchedule();
MutableTrigger trig = scheduleBuilder.build();
trig.setCalendarName(calendarName);
trig.setDescription(description);
trig.setStartTime(startTime);
trig.setEndTime(endTime);
if(key == null)
key = new TriggerKey(Key.createUniqueName(null), null);
trig.setKey(key);
if(jobKey != null)
trig.setJobKey(jobKey);
trig.setPriority(priority);
if(!jobDataMap.isEmpty())
trig.setJobDataMap(jobDataMap);
return (T) trig;
}
/**
* @param name 设置触发器名称
*/
public TriggerBuilder<T> withIdentity(String name) {
key = new TriggerKey(name, null);
return this;
}
/**
* @param name 设置触发器名称
* @param gourp 设置触发器分组
*/
public TriggerBuilder<T> withIdentity(String name, String group) {
key = new TriggerKey(name, group);
return this;
}
/**
* @param triggerKey 触发器键(作用:也是用来设置触发器名称和分组的)
*/
public TriggerBuilder<T> withIdentity(TriggerKey triggerKey) {
this.key = triggerKey;
return this;
}
/**
* @param triggerDescription 触发器的描述
*/
public TriggerBuilder<T> withDescription(String triggerDescription) {
this.description = triggerDescription;
return this;
}
/**
* @param triggerPriority 触发器的优先级,默认为5
*/
public TriggerBuilder<T> withPriority(int triggerPriority) {
this.priority = triggerPriority;
return this;
}
/**
* 通过日历修改
* @param calName 日历名称,目录7.4中使用过这个方法
*/
public TriggerBuilder<T> modifiedByCalendar(String calName) {
this.calendarName = calName;
return this;
}
/**
* 设置触发器开始的时间
* @param triggerStartTime 可以为任何时间段
*/
public TriggerBuilder<T> startAt(Date triggerStartTime) {
this.startTime = triggerStartTime;
return this;
}
/**
* 现在就开始触发(系统时间)
*/
public TriggerBuilder<T> startNow() {
this.startTime = new Date();
return this;
}
/**
* 触发器结束时间
* @param triggerEndTime 设置你想要触发器结束的时间
*/
public TriggerBuilder<T> endAt(Date triggerEndTime) {
this.endTime = triggerEndTime;
return this;
}
/**
* 正题来了,四种触发器这里都可以进行设置
* @param dailyTimeIntervalSchedule() 日期间隔触发器
* @param simpleSchedule() 简单触发器
* @param cronSchedule(String cronExpression) 定时触发器
* @param calendarIntervalSchedule() 日历间隔触发器
*/
@SuppressWarnings("unchecked")
public <SBT extends T> TriggerBuilder<SBT> withSchedule(ScheduleBuilder<SBT> schedBuilder) {
this.scheduleBuilder = schedBuilder;
return (TriggerBuilder<SBT>) this;
}
public TriggerBuilder<T> forJob(JobKey keyOfJobToFire) {
this.jobKey = keyOfJobToFire;
return this;
}
/**
* 设置想要触发的Job
* @param jobName 作用名字
*/
public TriggerBuilder<T> forJob(String jobName) {
this.jobKey = new JobKey(jobName, null);
return this;
}
/**
* 设置想要触发的作业
* @param jobName 作业名称
* @param jobGroup 作业所在分组
*/
public TriggerBuilder<T> forJob(String jobName, String jobGroup) {
this.jobKey = new JobKey(jobName, jobGroup);
return this;
}
/**
* 传入一个已经创建的jobDetail对象,前提是这个对象设置了名字
*/
public TriggerBuilder<T> forJob(JobDetail jobDetail) {
JobKey k = jobDetail.getKey();
if(k.getName() == null)
throw new IllegalArgumentException("The given job has not yet had a name assigned to it.");
this.jobKey = k;
return this;
}
/**往下都是设置JobDataMap的键-值队数据,具体去看目录6.1-6.2
---------------------------------------------------------------
public TriggerBuilder<T> usingJobData(String dataKey, String value) {
jobDataMap.put(dataKey, value);
return this;
}
public TriggerBuilder<T> usingJobData(String dataKey, Integer value) {
jobDataMap.put(dataKey, value);
return this;
}
public TriggerBuilder<T> usingJobData(String dataKey, Long value) {
jobDataMap.put(dataKey, value);
return this;
}
public TriggerBuilder<T> usingJobData(String dataKey, Float value) {
jobDataMap.put(dataKey, value);
return this;
}
public TriggerBuilder<T> usingJobData(String dataKey, Double value) {
jobDataMap.put(dataKey, value);
return this;
}
public TriggerBuilder<T> usingJobData(String dataKey, Boolean value) {
jobDataMap.put(dataKey, value);
return this;
}
public TriggerBuilder<T> usingJobData(JobDataMap newJobDataMap) {
// add any existing data to this new map
for(String dataKey: jobDataMap.keySet()) {
newJobDataMap.put(dataKey, jobDataMap.get(dataKey));
}
jobDataMap = newJobDataMap; // set new map as the map to use
return this;
}
}
*/
关于Quartz目前介绍到此,因为工作原因以后会出进阶版本,各位铁汁觉得写得还行对你有帮助的话,可以动动小手点点关注点点赞,编写不易转载请标注出处。