Quartz快速从入门到精通,不实用你给我寄刀片

1. Quartz 概述

​ Quartz 是 OpenSymphony 开源组织在任务调度领域的一个开源项目,完全基于 Java 实现。该项目于 2009 年被 Terracotta 收购,目前是 Terracotta 旗下的一个项目。读者可以到 http://www.quartz-scheduler.org/站点下载 Quartz 的发布版本及其源代码。

2. Quartz特点

​ 作为一个优秀的开源调度框架,Quartz 具有以下特点:

  1. 强大的调度功能,例如支持丰富多样的调度方法,可以满足各种常规及特殊需求;

  2. 灵活的应用方式,例如支持任务和调度的多种组合方式,支持调度数据的多种存储方式;

  3. 分布式和集群能力,Terracotta 收购后在原来功能基础上作了进一步提升。

    另外,作为 Spring 默认的调度框架,Quartz 很容易与 Spring 集成实现灵活可配置的调度功能。

quartz调度核心元素

  1. Scheduler:任务调度器,是实际执行任务调度的控制器。在spring中通过SchedulerFactoryBean封装起来。
  2. Trigger:触发器,用于定义任务调度的时间规则,有SimpleTrigger,CronTrigger,DateIntervalTrigger和NthIncludedDayTrigger,其中CronTrigger用的比较多,本文主要介绍这种方式。CronTrigger在spring中封装在CronTriggerFactoryBean中。
  3. Calendar:它是一些日历特定时间点的集合。一个trigger可以包含多个Calendar,以便排除或包含某些时间点。
  4. JobDetail:用来描述Job实现类及其它相关的静态信息,如Job名字、关联监听器等信息。在spring中有JobDetailFactoryBean和 MethodInvokingJobDetailFactoryBean两种实现,如果任务调度只需要执行某个类的某个方法,就可以通过MethodInvokingJobDetailFactoryBean来调用。
  5. 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 核心元素之间的关系如下图所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6eRj3gAi-1599753339384)(E:\Users\zhoug\Desktop\y2.技术笔记\Quartz\image\08195538_gUkq.gif)]

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(日历间隔触发器)

目前已经简单了解了两种触发器,“简单触发器”和“计时触发器”,下面这两个触发器不常用但也可以了解一下

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-G8cQbikA-1599753339394)(E:\Users\zhoug\Desktop\y2.技术笔记\Quartz\image\QQ截图20200910091654.png)]

在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()的简便之处。

### [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IP6HmuX2-1599753339400)(E:\Users\zhoug\Desktop\y2.技术笔记\Quartz\image\7a4b44536eb8091b05c1c3aed7462fc8ea3e0b4b86711a827.jpg)]

长白山压弯王要压弯了,系好安全带,上车

//当调用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目前介绍到此,因为工作原因以后会出进阶版本,各位铁汁觉得写得还行对你有帮助的话,可以动动小手点点关注点点赞,编写不易转载请标注出处。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QGD1hp1m-1599753339407)(E:\Users\zhoug\Desktop\y2.技术笔记\程序员表情包\5D923F51AAA759F1DAA373CA1C630A49.jpg)]

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值