简单Quartz学习笔记

文章目录

  • 前言
  • Quartz 框架
  • Quartz API
  • Quartz 数据库表
  • 搭建任务调度工程的简单流程
    • 1. 创建 Job 类
    • 2. 创建 JobDetail
    • 3. 创建触发器 Trigger
      • SimpleTrigger
      • CronTrigger
    • 4. 创建调度器 Scheduler
  • 任务的执行方式:串行 / 并行
    • 防止并行
    • 并行
  • 触发器的 misfire 机制
    • CronTrigger 使用的策略
    • 在触发器的定义中选择策略:TriggerBuilder.newTrigger()
  • Trigger 触发器状态
  • 异步通知
    • 一个Job绑定多个触发器
  • 创建任务的步骤优化
  • 监听器 Listener
    • JobListener
    • TriggerListener
    • SchedulerListener
  • Quartz 常见问题
    • Trigger does not reference given job!


前言

新手向笔记,来源多为网上博客例子。


SpringBoot整合Quartz简单完整例子
SpringBoot整合Quartz作为调度中心完整实用例子
spring-boot-2.0.3之quartz集成,不是你想的那样哦!
精通SpringBoot——第十篇:使用Quartz实现动态配置定时任务
quartz初次使用踩坑记


Quartz 框架

Quartz任务调度(1)概念例析快速入门

  • 在现实开发中,我们常常会遇到需要系统在特定时刻完成特定任务的需求,通过引介增强来简单地模拟实现了一个定时器。它可能只需要我们自己维护一条线程就足以实现定时监控。
    但在实际开发中,我们遇到的需求会复杂很多,可能涉及多点任务调度,需要我们多线程并发协作、线程池的维护、对运行时间规则进行更细粒度的规划、运行线程现场的保持与恢复等等。如果我们选择自己来造轮子,可能会遇到许多难题。这时候,引入Quartz任务调度框架会是一个很好的选择,它:
  1. 允许我们灵活而细粒度地设置任务触发时间,比如常见的每隔多长时间,或每天特定时刻、特定日子(如节假日),都能灵活地配置这些成相应时间点来触发我们的任务
  2. 提供了调度环境的持久化机制,可以将任务内容保存到数据库中,在完成后删除记录,这样就能避免因系统故障而任务尚未执行且不再执行的问题。
  3. 提供了组件式的侦听器、各种插件、线程池等。通过侦听器,我们可以引入我们的事件机制,配合上异步调用,既能为我们的业务处理类解耦,同时还可能提升用户体验

Quartz API

  • 基本要素:
    Scheduler:任务调度器。所有的调度都是由它控制
    Job:由希望由调度程序执行的组件实现的接口
    JobDetail:用于定义任务的实例,如任务名称
    Trigger(即触发器):定义触发的条件。如,每隔1秒执行一次
  • JobBuilder:用于定义/构建JobDetail实例,用于定义作业的实例
    TriggerBuilder :用于定义/构建触发器实例

Quartz 数据库表

注意:cron方式需要用到的4张数据表:
qrtz_job_details;qrtz_triggers;qrtz_cron_triggers;qrtz_fired_triggers

  • QRTZ_JOB_DETAILS
    存储每一个已配置的任务Job的详细信息

  • QRTZ_TRIGGERS
    存储已配置的触发器Trigger的信息

  • QRTZ_CRON_TRIGGERS
    存储Cron Trigger,包括Cron表达式和时区信息

  • QRTZ_FIRED_TRIGGERS
    存储每个正在执行的触发器Trigger相关的状态信息,以及相联Job的执行信息

  • QRTZ_SCHEDULER_STATE
    记录 调度器(每个机器节点)的生命状态

  • QRTZ_LOCKS
    记录程序的悲观锁(防止多个节点同时执行同一个定时任务)

  • QRTZ_SIMPLE_TRIGGERS
    存储已配置的Simple Trigger的信息

  • QRTZ_SIMPROP_TRIGGERS
    存储CalendarIntervalTrigger和DailyTimeIntervalTrigger两种类型的触发器

  • QRTZ_BLOB_TRIGGERS
    Trigger作为Blob类型存储(用于Quartz用户用JDBC创建他们自己定制的Trigger类型,JobStore并不知道如何存储实例的时候)

  • QRTZ_CALENDARS
    以Blob类型存储Quartz的Calendar日历信息,quartz可配置一个日历来指定一个时间范围

  • QRTZ_PAUSED_TRIGGER_GRPS
    存储已暂停的Trigger组的信息


搭建任务调度工程的简单流程

知乎:最牛逼的任务调度工具 | Quartz

1. 创建 Job 类

描述任务要执行的具体内容,任务要做什么,比如打印输出文字等等
注:Job类必须单独一个文件,必须为public

import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

/**
 * 任务执行的内容
 * 注:必须单独一个文件
 * 注:必须为public
 **/
public class MyJob1 implements Job {
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        System.out.println("...abc打印123456...");
    }
}

2. 创建 JobDetail

任务的信息,如名称、组别、描述等

// 2. 创建 JobDetail
JobDetail jobDetail =JobBuilder.newJob(MyJob1.class)
                            .withIdentity("job1", "group1")
                            .withDescription("MyJob1Detail des.")
                            .build();

3. 创建触发器 Trigger

触发器的信息,如名称、组别、描述等
触发器开始执行的时间、周期、间隔等;
TriggerBuilder.newTrigger() 建立;接着定义各种属性;

SimpleTrigger

普通的触发器

// 3. 创建触发器Trigger
Trigger trigger =
       TriggerBuilder.newTrigger()
               .withIdentity("trigger1", "group")
               .withDescription("trigger1 des.")
//               .startNow()
				.startAt(DateBuilder.dateOf(10, 00, 00))
               .endAt(DateBuildr.dateOf(11,00,00))
               .withSchedule(SimpleScheduleBuilder
                       .simpleSchedule()
                       // 间隔
                       .withIntervalInSeconds(1)
                       // 重复次数,7次
//                       .repeatForever()
                       .withRepeatCount(7)
                       // 若失效,之后再恢复并马上执行
                       .withMisfireHandlingInstructionFireNow()
               )
               .forJob("job1", "group1")
               .build();
  • 使用.forJob(“job1”, “group1”):IDEA会显示 JobName、JobGruop 灰色的信息提示字样
    简单Quartz学习笔记_第1张图片

CronTrigger

使用基于日历的触发机制的触发器,cron表达式

 // 使用基于日历的触发机制的触发器 CronTrigger
 String cron1 = "* * * * * ? *";
 CronTrigger cronTrigger = (CronTrigger)TriggerBuilder.newTrigger()
         .withIdentity("cron trigger 1", "group")
         .withDescription("cron trigger 1 des.")
         .startNow()
         .withSchedule(CronScheduleBuilder
                 .cronSchedule(cron1)
         )
         .build();

4. 创建调度器 Scheduler

调度器的创建模式:一般用 工厂模式 SchedulerFactory 创建
调度器所要调度的 任务的信息(jobDetail)、所要调度的 触发器(trigger)
开始执行任务: scheduler.start();

// 4. 创建调度器 Scheduler
SchedulerFactory schedulerFactory = new StdSchedulerFactory();
            Scheduler scheduler = schedulerFactory.getScheduler();
            scheduler.scheduleJob(jobDetail, trigger);
            scheduler.start();

任务的执行方式:串行 / 并行

Quartz框架(三)—任务的并行/串行执行

防止并行

使用注解方式:注解在 org.quartz 包的内部,注解在Job类上边
@DisallowConcurrentExecution:禁止任务并发执行
@PersistJobDataAfterExecution:正常执行完任务后,JobDataMap 中的数据应该被改动,以被下一次调用时使用

import lombok.extern.log4j.Log4j2;
import org.quartz.*;

/**
* Job 是 定时任务的具体执行逻辑
* JobDetail 是 定时任务的定义
 * 注解 DisallowConcurrentExecution:禁止任务并发执行
 * 注解 PersistJobDataAfterExecution:正常执行完任务后,JobDataMap 中的数据应该被改动,以被下一次调用时用
* @author EalenXie
* @date 2019/7/10
*/
@DisallowConcurrentExecution
@PersistJobDataAfterExecution
@Log4j2
public class SayHelloJobLogic implements Job {

    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        // 任务具体逻辑
        log.info("任务(共5秒)开始 SayHelloJob.execute , start... ");
        
		// 延时5秒
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
        log.info("任务(共5秒)结束 SayHelloJob.execute , end... ");
    }
}

并行

较新版本的quartz,StatefulJob 这个类已经过期,所以有状态的Job类,指的是没有并行执行那两个注解(@DisallowConcurrentExecution、@PersistJobDataAfterExecution)的Job类、


触发器的 misfire 机制

Quartz框架(四)—misfire处理机制
任务框架quartz的misfire的理解

在Quartz中,当一个持久化的触发器因为:

  1. 调度器被关闭; scheduler shuwdown
  2. 线程池没有可用线程; pool no free threads
  3. 项目重启; project restart
  4. 任务的串行(并发)执行; jobs excute concurrently / serially
    而错过激活时间,就会发生激活失败(misfire)。 miss trigger time. / misfire

CronTrigger 使用的策略

  1. 所有的misfile任务马上执行
  2. CornTrigger默认策略,合并部分misfire,正常执行下一个周期的任务。
    比如在恢复后,共有两个misFire,则合并为一个misFire,于是只执行这一个misFire
  3. 所有的misFire都不管,执行下一个周期的任务。
// 所有的misfile任务马上执行
public static final int MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY = -1;

// 在Trigger中默认选择 MISFIRE_INSTRUCTION_FIRE_ONCE_NOW 策略
public static final int MISFIRE_INSTRUCTION_SMART_POLICY = 0;

// CornTrigger默认策略,合并部分misfire,正常执行下一个周期的任务。
// 比如在恢复后,共有两个misFire,则合并为一个misFire,于是只执行这一个misFire
public static final int MISFIRE_INSTRUCTION_FIRE_ONCE_NOW = 1;

// 所有的misFire都不管,执行下一个周期的任务。
public static final int MISFIRE_INSTRUCTION_DO_NOTHING = 2;

对应 CronTrigger 类的内部源代码:

    public CronScheduleBuilder withMisfireHandlingInstructionIgnoreMisfires() {
        this.misfireInstruction = -1;
        return this;
    }
    public CronScheduleBuilder withMisfireHandlingInstructionFireAndProceed() {
        this.misfireInstruction = 1;
        return this;
    }

    public CronScheduleBuilder withMisfireHandlingInstructionDoNothing() {
        this.misfireInstruction = 2;
        return this;
    }

在触发器的定义中选择策略:TriggerBuilder.newTrigger()

TriggerBuilder.newTrigger()
	// trigger 名称、组名
	.withIdentity(jobKey.getName(), jobKey.getGroup())
	// trigger 描述
	.withDescription(description)
	// 使用 Cron 和对应的调度器 CronScheduleBuilder
	.withSchedule(CronScheduleBuilder
	        .cronSchedule(cronExpression)
	        // 选择 misFire 策略
	
	        // 1.所有的 misFire 都不管,执行下一个周期的任务。
	        .withMisfireHandlingInstructionDoNothing()
	        
			// 不选择任何策略的话,则默认为该策略
	        // 2.CronTrigger 默认策略,合并部分misfire,正常执行下一个周期的任务
//            .withMisfireHandlingInstructionFireAndProceed()
	
	        // 3. 所有的misfire任务马上执行
//            .withMisfireHandlingInstructionIgnoreMisfires()
	
	    )
	
	.usingJobData(jobDataMap)
	.build();

Trigger 触发器状态

Quartz框架(六)— Trigger状态转换

正常获取、触发任务执行的流程:

一个触发器只能绑定一个Job,但是一个Job可以有多个触发器

调度器线程执行的时候,首先从triggers表中获取状态为WAITING,并且将要触发的Trigger。然后将WAITING状态更新为ACQUIRED,表示该触发器抢占到了,防止其他调度器(实例)抢占。然后插入触发器信息以及实例名到FRIED_TRIGGERS表中,状态为ACQUIRED。前面的更新和后面的插入是在一个事务中进行的。

该触发器抢占到任务后,等待触发时间的到来。

执行时间到来后,每触发一次任务,都会在 FIRED_TRIGGERS 表中创建一条记录,并且状态为 EXECUTING

如果任务允许并发执行,此时TRIGGERS表里的状态更新为 WAITING,PAUSED,COMPLETE(不需要执行)

如果任务不允许并发执行,还会把Triggers表里的状态更新为BLOCK或PAUSED_BLOCK。

  • 注意:Triggers表更新时根据 任务名 和 任务所属组名 来更新,而不是 触发器名称 和 触发器组名 来更新的。
    这就解决了一个任务有多个触发器的并发问题;然后触发器线程会创建一个执行环境来执行任务,以便在任务执行完成后更新触发器的状态。任务执行完成后,在一个事务中触发器状态更新为WAITING,删除FIRED_TRIGGERS表里对应的记录。

  • 如何避免多个节点执行同一个任务
    qrtz_trigger 表中有 NEXT_FIRE_TIME 字段(下一次触发时间)。每个任务在即将执行的时候,获取qrtz_locks 表中的行级锁,开启一个事务(更新 qrtz_trigger 表状态为ACQUIRED,并且插入 qrtz_fire_trigger 一条数据,起始状态为 ACQUIRED)。


异步通知

Quartz框架(八)— Quartz实现异步通知

一个Job绑定多个触发器

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import com.example.execute.SayHelloJobLogic;
import lombok.extern.log4j.Log4j2;

import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import static org.quartz.TriggerBuilder.newTrigger;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Calendar;

/**
 * 测试类
 *@author 小胖
 *@date 2019/07
 **/
@Log4j2
@RunWith(SpringRunner.class)
@SpringBootTest
public class TestQuartzForMoreJob {

    /**
    * 一个任务Job 绑定多个触发器
    * @author 小胖
    * @date 2019/07
    */
    @Test
    public void MultiTriggersTest() {

        // 设置执行次数:1次
        SimpleScheduleBuilder simpleScheduleBuilder =
                SimpleScheduleBuilder.repeatSecondlyForTotalCount(1);

        // 定义任务信息:前面列出的占用时间5秒的打印文字Job
        JobDetail jobDetail = JobBuilder.newJob(SayHelloJobLogic.class)
                .withIdentity("job1", "job_group1")
                .storeDurably()
                .build();
                
		// 定义时间格式变量
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        // 当前时间
        Date date = new Date();
        // 时间操作,当前时间加 5秒
        Date date1 = addSecond(date, 5);
        // 时间操作,当前时间加 15秒
        Date date2 = addSecond(date, 15);

        // 日志打印
        // 当前时间,      如 2020-02-20 09:00:00
        log.info("获取到任务的时间:" + simpleDateFormat.format(date));
        // 当前时间 + 5s, 如 2020-02-20 09:00:05
        log.info("第一次通知的时间:" + simpleDateFormat.format(date1));
        // 当前时间 + 15s,如 2020-02-20 09:00:15
        log.info("第二次通知的时间:" + simpleDateFormat.format(date2));

        // 触发器1:当前时间 + 5s
        SimpleTrigger trigger1 = newTrigger().withIdentity("trigger1", "group1")
                .withSchedule(simpleScheduleBuilder)
                .startAt(date1)
                .forJob(new JobKey("job1", "job_group1"))
                .withDescription("trigger1, + 5s ")
                .build();

        // 触发器2:当前时间 + 15s
        SimpleTrigger trigger2 = newTrigger().withIdentity("trigger2", "group1")
                .withSchedule(simpleScheduleBuilder)
                .startAt(date2)
                .forJob(new JobKey("job1", "job_group1"))
                .withDescription("trigger2, + 15s ")
                .build();

        try {
            Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();

            // 多触发器关联
            scheduler.scheduleJob(jobDetail, trigger1);
            scheduler.scheduleJob(trigger2);
            scheduler.start();

            // 任务完成,再过100s,关闭调度器
            Thread.sleep(100000);
            scheduler.shutdown();

        } catch (SchedulerException | InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
    * 时间操作,对一个具体的时间增加多少秒
    * @param date 时间
     * @param second 增加的秒数
    * @return java.util.Date
    * @author 小胖
    * @date 2019/07
    */
    public static Date addSecond(Date date, int second) {
        java.util.Calendar calendar = java.util.Calendar.getInstance();
        calendar.setTime(date);
//        calendar.add(Calendar.MINUTE, minute);
        calendar.add(Calendar.SECOND, second);
        return calendar.getTime();
    }
    
}

测试截图:
在这里插入图片描述
简单Quartz学习笔记_第2张图片
简单Quartz学习笔记_第3张图片


创建任务的步骤优化

SpringBoot整合Quartz简单完整例子

/**
* Quartz 里面 一个最简单,最基本的定时任务 应该包含以下必要基本属性
* 注意 : 这里只是方便演示,故写此类进行简单说明, 可针对自身业务对此类进行扩展
* @author EalenXie
* @date 2019/07
*/
@Data
@Builder
public class JobInfo {
    /**
     * 定时任务 的名字和分组名
     */
    @NotNull(message = "定时任务的 名字 和 组名 坚决不为空")
    private JobKey jobKey;

    /**
     * 定时任务 的描述(可以定时任务本身的描述,也可以是触发器的)
     */
    private String description;

    /**
     * 定时任务 的执行cron
     */
    @NotEmpty(message = "定时任务的执行cron 不能为空")
    private String cronExpression;

    /**
     * 定时任务 的元数据
     */
    private Map<?, ?> jobDataMap;

    /**
     * 定时任务 的 具体执行逻辑类
     */
    @NotNull(message = "定时任务的具体执行逻辑类 坚决不能为空")
    private Class<? extends Job> jobClass;
}
/**
* 创建和启动 定时任务
* @param jobInfo 定时任务
*/
public void startJob(JobInfo jobInfo) throws SchedulerException {
	// 1.定时任务 的 名字和组名
	JobKey jobKey = jobInfo.getJobKey();
	// 2.定时任务 的 元数据
	JobDataMap jobDataMap = getJobDataMap(jobInfo.getJobDataMap());
	// 3.定时任务 的 描述
	String description = jobInfo.getDescription();
	// 4.定时任务 的 逻辑实现类
	Class<? extends Job> jobClass = jobInfo.getJobClass();
	// 创建 JobDetail 、Trigger
	JobDetail jobDetail = getJobDetail(jobKey, description, jobDataMap, jobClass);
	Trigger trigger = getTrigger(jobKey, description, jobDataMap, cron);
	// 启动调度器
	scheduler.scheduleJob(jobDetail, trigger);
}

/**
* 获取定时任务的定义
 * JobDetail是任务的定义, Job是任务的执行逻辑
* @param jobKey 定时任务的名称 组名
 * @param description 定时任务的 描述
 * @param jobDataMap 定时任务的 元数据
 * @param jobClass 定时任务的 真正执行逻辑定义类
* @return org.quartz.JobDetail
*/
public JobDetail getJobDetail(JobKey jobKey,
                              String description,
                              JobDataMap jobDataMap,
                              Class<? extends Job> jobClass) {
    return JobBuilder.newJob(jobClass)
            .withIdentity(jobKey)
            .withDescription(description)
            .setJobData(jobDataMap)
            .usingJobData(jobDataMap)
            .requestRecovery()
            .storeDurably()
            .build();
}
/**
* 获取Trigger (Job的触发器,执行规则)
* @param jobKey 定时任务的名称 组名
 * @param description 定时任务的 描述
 * @param jobDataMap 定时任务的 元数据
 * @param cronExpression 定时任务的 执行cron表达式
* @return org.quartz.Trigger
*/
public Trigger getTrigger(JobKey jobKey,
                          String description,
                          JobDataMap jobDataMap,
                          String cronExpression) {
    return TriggerBuilder.newTrigger()
            // trigger 名称、组名
            .withIdentity(jobKey.getName(), jobKey.getGroup())
            // trigger 描述
            .withDescription(description)
            // 
            .withSchedule(CronScheduleBuilder
                    .cronSchedule(cronExpression)
                    // 选择 misFire 策略:
                    // 1. 所有的 misFire 都不管,执行下一个周期的任务。
                    .withMisfireHandlingInstructionDoNothing()

                    // 2.CronTrigger 默认策略,合并部分misfire,正常执行下一个周期的任务
//                        .withMisfireHandlingInstructionFireAndProceed()

                    // 3. 所有的misfile任务马上执行
//                        .withMisfireHandlingInstructionIgnoreMisfires()

                )
            .usingJobData(jobDataMap)
            .build();
}
/**
* 获取任务的元数据
* @param map 不限量的(序列化的)数据对象
* @return org.quartz.JobDataMap
*/
public JobDataMap getJobDataMap(Map<?, ?> map) {
    return map == null ? new JobDataMap() : new JobDataMap(map);
}

监听器 Listener

用于当任务调度中你所关注事件发生时,能够及时获取这一事件的通知。类似于任务执行过程中的邮件、短信类的提醒
对Job(任务)建立一个监听器,分别对任务执行 《之前, 之后, 取消》 3个阶段进行监听;
实现监听器需要实现JobListener接口,然后注册到Scheduler上;

  • 全局监听器非全局监听器,二者的区别在于:
    全局监听器:能够接收到所有的Job/Trigger的事件通知
    非全局监听器:只能接收到在其上注册的Job或Trigger的事件,不在其上注册的Job或Trigger则不会进行监听

Quartz框架(十)监听
Quartz使用(4) - Quartz监听器Listerner
【Quartz】 JobListener、Triggerlistener、SchedulerListener介绍与使用


JobListener

任务调度过程中,与任务Job相关的事件包括:Job开始要执行的提示;;Job执行完成的提示灯
Quartz任务调度(4) JobListener分版本超详细解析

// (1) Job Listener
// 监听器 Job Listener
MyJobListener myJobListener = new MyJobListener();

// 指定任务的 Job Listener
KeyMatcher<JobKey> jobMatcher = KeyMatcher.keyEquals(jobDetail.getKey());
// 指定任务的 Job Listener
KeyMatcher<JobKey> cronJobMatcher = KeyMatcher.keyEquals(nonStateJobDetail.getKey());

// 指定组的 Job Listener
GroupMatcher<JobKey> groupJobMatcher = GroupMatcher.jobGroupEquals("group2");

// 创建并注册,局部的 Job Listener
//            scheduler.getListenerManager().addJobListener(myJobListener, jobMatcher);
//            scheduler.getListenerManager().addJobListener(myJobListener, cronJobMatcher);

// 创建并注册,全局的 Job Listener
scheduler.getListenerManager().addJobListener(myJobListener, EverythingMatcher.allJobs());

TriggerListener

任务调度过程中,与触发器Trigger相关的事件包括:触发器触发、触发器未正常触发、触发器完成等。
Quartz任务调度(5) TriggerListener分版本超详细解析

// (2) Trigger Listener
MyTriggerListener myTriggerListener = new MyTriggerListener("MyTriggerListener");

// 获取 triggerKey
TriggerKey triggerKey = trigger.getKey();
TriggerKey cronTriggerKey = cronTrigger.getKey();

// 指定任务的 Trigger Listener
KeyMatcher<TriggerKey> triggerMatcher = KeyMatcher.keyEquals(triggerKey);
KeyMatcher<TriggerKey> cronTriggerMatcher = KeyMatcher.keyEquals(cronTriggerKey);

// 一个特定组的 Trigger Listener
//            GroupMatcher groupTriggerMatcher = GroupMatcher.groupEquals("group1");
//            GroupMatcher groupTriggerMatcher1 = GroupMatcher.groupStartsWith("g");
//            GroupMatcher groupTriggerMatcher2 = GroupMatcher.groupEndsWith("2");

// 创建并注册,局部的 Trigger Listener
//            scheduler.getListenerManager().addTriggerListener(myTriggerListener,triggerMatcher);
//            scheduler.getListenerManager().addTriggerListener(myTriggerListener, cronTriggerMatcher);

// 全局的 Trigger Listener
scheduler.getListenerManager().addTriggerListener(myTriggerListener, EverythingMatcher.allTriggers());

SchedulerListener

SchedulerListener会在Scheduler的生命周期中关键事件发生时被调用。
与Scheduler有关的事件包括:增加一个Job/Trigger,删除一个Job/Trigger,Scheduler发生严重错误,关闭Scheduler等。
Quartz任务调度(6) SchedulerListener分版本超详细解析

// (3) Scheduler Listener
MySchedulerListener mySchedulerListener = new MySchedulerListener();
scheduler.getListenerManager().addSchedulerListener(mySchedulerListener);

// 5. 启动任务
// 启动调度器
log.info("开启调度器 scheduler start...");
scheduler.start();

// 20秒
try {
        Thread.sleep(20000);
} catch (InterruptedException e) {
        e.printStackTrace();
}

// 暂停Job
scheduler.pauseJob(jobDetail.getKey());
scheduler.pauseJob(nonStateJobDetail.getKey());

// 绑定新的 CronTrigger
String newCron = "0/15 * * * * ? ";
CronTrigger cronTrigger1 = TriggerBuilder.newTrigger()
        .withIdentity("newCronTrigger", "group2")
        .withDescription("a new cron")
        .startNow()
        .withSchedule(CronScheduleBuilder.cronSchedule(newCron))
        .forJob("nonStatedJob", "group2")
        .build();
TriggerKey triggerKey1 = cronTrigger1.getKey();
scheduler.rescheduleJob(triggerKey1, cronTrigger1);

// 恢复运行Job
scheduler.resumeJob(jobDetail.getKey());

// 20秒
try {
        Thread.sleep(20000);
} catch (InterruptedException e) {
        e.printStackTrace();
}

// 删除Job
scheduler.deleteJob(jobDetail.getKey());

// 卸载触发器
scheduler.unscheduleJob(triggerKey);
scheduler.unscheduleJob(cronTriggerKey);

// 6. 停止任务
// 停止调度器
log.info("停止调度器 scheduler shutdown...");
scheduler.shutdown();

Quartz 常见问题

Trigger does not reference given job!

java quartz框架创建定时任务异常: Trigger does not reference given job!
Quartz调度器-触发器不引用给定的作业:Trigger does not reference given job

  • 检查 JobDetail 定义的 .withIdentity() 的内容,是否与 Trigger 定义的 .forJob() 的内容是否相同,不同则会报错。
JobDetail jobDetail =
        JobBuilder.newJob(MyJob1.class)
                .withIdentity("job1", "group1")
                .withDescription("jobDetail des.")
                .build();
                
Trigger trigger =
        TriggerBuilder.newTrigger()
                .withIdentity("SimpleTrigger1", "SimpleGroup")
                .withDescription("SimpleTrigger1 des.")
                .startNow()
                .withSchedule(SimpleScheduleBuilder
                        .simpleSchedule()
                        // 间隔
                        .withIntervalInSeconds(2)
                        // 重复次数
                        .repeatForever()
                        // 若失效,之后再恢复并马上执行
                        .withMisfireHandlingInstructionFireNow()
                )
                // 检查此处是否与将要绑定的JobDetail中的Job名称和组名称是否相同
                .forJob("job1", "group1")
                .build();
    
SchedulerFactory schedulerFactory = new StdSchedulerFactory();
Scheduler scheduler = schedulerFactory.getScheduler();

// 监听器
MyJobListener myJobListener = new MyJobListener();

KeyMatcher<JobKey> keyMatcher = KeyMatcher.keyEquals(jobDetail.getKey());
scheduler.getListenerManager().addJobListener(myJobListener, keyMatcher);

// 若报错(Trigger does not reference given job)一般会指向这里
scheduler.scheduleJob(jobDetail, trigger);

你可能感兴趣的:(Spring,JAVA)