Quartz定时任务框架核心概念详解【附部分源码截图】

Quartz定时任务框架

Quartz API,Jobs和Triggers_w3cschool;【学习链接】

参考:http://t.csdnimg.cn/AfVJR;

01-快速入门

1.1-Quartz 核心概念

我们需要明白 Quartz 的几个核心概念,这样理解起 Quartz 的原理就会变得简单了。

Quartz API的关键接口是:

  • Scheduler - 与调度程序交互的主要API。
  • Job - 你想要调度器执行的任务组件需要实现的接口
  • JobDetail - 用于定义作业的实例【这个JobDetail里其实就封装了我们自己实现Job接口的实现类(也就是MyJob类)】。
  • Trigger(即触发器) - 定义执行给定作业的计划的组件【也就是定义了一些任务执行的策略,比如时间等】。
  • JobBuilder - 用于定义/构建 JobDetail 实例,用于定义作业的实例。
  • TriggerBuilder - 用于定义/构建触发器实例。
  • Scheduler 的生命期,从 SchedulerFactory 创建它时开始,到 Scheduler 调用shutdown() 方法时结束;Scheduler 被创建后,可以增加、删除和列举 Job 和 Trigger,以及执行其它与调度相关的操作(如暂停 Trigger)。但是,Scheduler 只有在调用 start() 方法后,才会真正地触发 trigger(即执行 job)

其整体架构如下图

在这里插入图片描述

1.2-快速入门代码

1.先使用springboot脚手架创建一个springboot项目(不再赘述)

2.导入依赖

<--注意这样导入只使用与springboot2.0以上版本 -->
<dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-quartz</artifactId>
</dependency>

3.定义一个自己的job类实现job接口

public class MyJob implements Job {

    static int count = 0;


    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        System.out.println("任务进行" + count++ + "次");
    }
}

4.编写测试类

@SpringBootTest
class QuartzApplicationTests {

    @Test
    void contextLoads() throws SchedulerException, InterruptedException {
        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();

        scheduler.getContext().put("skey", "svalue");
        scheduler.start();


        JobDetail jobDetail = JobBuilder.newJob(MyJob.class).withIdentity("1", "h").build();

        Trigger trigger = TriggerBuilder.newTrigger()
                .withIdentity("1","h")
                .startNow()
                .withSchedule(SimpleScheduleBuilder.simpleSchedule()
                        .withIntervalInSeconds(2)
                        .repeatForever())
                .build();

        scheduler.scheduleJob(jobDetail, trigger);

        Thread.sleep(600000);
        scheduler.shutdown();
    }

}

5.运行可以看到我们自己定义的MyJob中的execute方法被定时执行了

在这里插入图片描述

小总结:

  1. 构建一个Scheduler,Scheduler里会放置Trigger【存储了任务调度规则】和JobDetail【存储了任务的内容】,
  2. Trigger和JobDetail两个通过Key【JobKey和TriggerKey】设置组别和名字,通过Key就可以将Trigger和JobDetail关联起来,值得注意的是Trigger里既有JobKey又有TriggerKey
  3. JobDetail里放置了我们自己定义的Job的实现类
  4. 通过上述操作,这样Scheduler就可以通过Trigger的规则拿到对应的JobDetail,从而调用相对应的Job实现类。

02-Quartz核心详解

2.1-Job 和 JobDetail

Job和JobDetail的关系,JobDetail里包含了Job的Class对象,具体源码体现如下:

在这里插入图片描述

可以看到JobDetail里包含了一个jobClass的属性,该属性就是对应Job的Class对象

2.1.1-Job

从代码层面来说,Job就是一个接口,我们使用Quartz定义任务时,其实就是定义了一个实现Job接口的实现类,我们任务需要做的内容就是定义在Job实现类重写的execute方法中。Job的具体源码如下

在这里插入图片描述

  • **Job实例:**一个自己定义的Job实例可以创建多个JobDeatil

  • Job状态与并发:

    • @DisallowConcurrentExecution:该注解放在我们自己定义的Job实现类上,告诉Quartz不能并发的执行这个Job实现类的多个JobDetail
    • @PersistJobDataAfterExecution:将该注解加在job实现类上,告诉Quartz在成功执行了job实现类的execute方法后(没有发生任何异常),更新所有JobDetail中JobDataMap的数据,使得该job(即JobDetail)在下一次执行的时候,JobDataMap中是更新后的数据,而不是更新前的旧数据
    • 值得注意的是,使用了**@PersistJobDataAfterExecution注解后最好同时使用@DisallowConcurrentExecution**,因为当一个Job的两个实例在并发的执行后,如果有一个JobDetail的JobDateMap改变了,因为使用了**@DisallowConcurrentExecution**注解,所以会去更新所有JobDetail的JobDateMap,如果此时有其他JobDetail在并发的执行,并且要使用JobDateMap,就会出现数据不一致的问题。
  • Job的其他特性:

    • **Durability:**如果一个job是非持久的,当没有活跃的trigger【意思就是一个Job关联的所有Trigger都不会被执行了】与之关联的时候,会被自动地从scheduler中删除。也就是说,非持久的job的生命期是由trigger的存在与否决定的;

      该属性通过如下代码设置:

      JobDetail jobDetail = JobBuilder.newJob(DumbJob.class)
                      .storeDurably(true)	//表示开启持久化
                      .build();
      

      在这里插入图片描述

    • **RequestsRecovery:**如果一个job是可恢复的,并且在其执行的时候,scheduler发生硬关闭(hard shutdown)(比如运行的进程崩溃了,或者关机了),则当scheduler重新启动的时候,该job会被重新执行。此时,该job的JobExecutionContext.isRecovering() 返回true。

      该属性通过如下代码设置:

      JobDetail jobDetail = JobBuilder.newJob(DumbJob.class)
                      .requestRecovery(true)	//开启可恢复
      

      在这里插入图片描述

2.1.2-JobDetail

JobDetail从代码上来说也是一个接口,我们创建Schedule之后,会将JobDeTail和Trigger放入到Schedule中,其中JobDetail就是为了方便Schedule调用我们自定义的Job提供了一系列的方法,方便将Job加入到任务调度器中,以及方便Schedule对我们定义的Job进行管理。JobDetail接口的源码如下:

在这里插入图片描述

2.2-Trigger

介绍:Trigger主要是用来定义任务调度规则的

2.2.1-Trigger的公共属性

所有类型的Trigger都有TriggerKey这个属性,表示Trigger的身份

Ttrigger的公共属性有:

  • 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.2.2-Trigger的优先级

比如,你有N个trigger需要同时触发,但Quartz只有Z个工作线程,优先级最高的Z个trigger会被首先触发。如果没有为trigger设置优先级,trigger使用默认优先级,值为5;priority属性的值可以是任意整数,正数、负数都可以,优先级的范围为1-10,优先级最高的为10

在代码层面通过withPriority(int)方法进行设置

SimpleTrigger trigger = TriggerBuilder.newTrigger()
                .withPriority(6)
                .build()

我们进入该方法的内部查看,其实是设置了TriggerBuilder的priority

在这里插入图片描述

我们再进入到TriggerBuilder的build()方法中查看,发现在构建器构建Trigger时将优先级给了对应的Trigger

在这里插入图片描述

2.2.3-日历示例(calendar)
2.2.4-Simple Trigger和CronTrigger

简单介绍一下这两者的区别主要是:CronTrigger可以定义指定的日历进行任务规则调度,Simple Trigger不能指定日历进行任务规则调度,主要用于一些简单的日历调度。具体的使用可以在使用的时候参考API。

2.3-Listeners监听器

2.3.1-JobListener

概述:Listener就是用来监听相对应的Job和Trigger的,我们可以点进去看一下JobListener的接口,包含了一下主要方法

在这里插入图片描述

  • 监听器名称:通过 getName() 方法,可以为每个监听器实例设置一个唯一的名称,以便在Quartz中进行标识和管理。
  • Job执行前监听jobToBeExecuted() 方法允许监听器在Job执行之前进行一些预处理操作,例如记录日志、获取执行前的状态等。
  • Job执行被否决监听jobExecutionVetoed() 方法用于处理Job被否决执行的情况,可以在此方法中执行一些清理或记录操作。
  • Job执行后监听jobWasExecuted() 方法用于处理Job执行完成后的事件,不论是成功完成还是出现异常。可以在此方法中处理执行后的清理工作、记录执行日志等。

关于执行前监听和执行后监听很好理解,我们着重介绍一下Job执行本否决是什么意思:

  1. TriggerListener 中的否决决策:

    • 在与Trigger关联的TriggerListener中,可以通过实现 vetoJobExecution() 方法来决定是否否决Job的执行。如果一个TriggerListener否决了Job的执行,那么调度器会通知所有相关的JobListener,并且不会执行这个Job。
  2. JobListener 中的 jobExecutionVetoed() 方法:

    • 在实现了 JobListener 接口的监听器中,如果Job被触发器触发时被否决执行,即没有实际执行Job的情况下,jobExecutionVetoed(JobExecutionContext context) 方法会被调用。这种情况通常源于其他监听器或Quartz内部的决策,例如另一个TriggerListener中调用了 vetoJobExecution() 方法。

代码示例:

1.创建JobListener

/**
 * @author hanLin.liu
 * @create 2024-08-07 10:25
 */
@Component
public class MyJobListener implements JobListener {
    @Override
    public String getName() {
        return "myJobListener";
    }

    @Override
    public void jobToBeExecuted(JobExecutionContext jobExecutionContext) {
        System.out.println("即将执行");
    }

    @Override
    public void jobExecutionVetoed(JobExecutionContext jobExecutionContext) {
        System.out.println("执行被否决");
    }

    @Override
    public void jobWasExecuted(JobExecutionContext jobExecutionContext, JobExecutionException e) {
        System.out.println("执行完成");
    }
}

2.在创建完Listener之后,我们需将这个Listener加入到schedule中

// 创建一个schedule
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();

//将Listener加入到schedule中
scheduler.getListenerManager().addJobListener(myJobListener, jobGroupEquals("liu"));
2.3.2-TriggerListener

我们点击去TriggerListener接口,可以看到有以下方法

在这里插入图片描述

接下来我们来解释一些各个方法的作用

  1. getName()
    • 作用:返回监听器的名称。
    • 用途:标识这个监听器实例,便于在配置和日志中识别。
  2. triggerFired(Trigger var1, JobExecutionContext var2)
    • 作用:在与Trigger关联的作业(Job)被调度执行时触发。
    • 参数:
      • var1:触发该事件的Trigger对象。
      • var2:当前JobExecutionContext对象,包含Job执行的上下文信息。
    • 用途:通常用于在Job实际执行之前执行一些准备工作或记录日志信息。
  3. vetoJobExecution(Trigger var1, JobExecutionContext var2)
    • 作用:决定是否否决(阻止)与Trigger关联的作业的执行。
    • 参数:
      • var1:触发该事件的Trigger对象。
      • var2:当前JobExecutionContext对象,包含Job执行的上下文信息。
    • 返回值true表示否决Job的执行,false表示允许Job的执行。
    • 用途:根据业务逻辑或条件来决定是否执行Job。如果返回true,Quartz将不会执行该Job,而是直接跳过。
  4. triggerMisfired(Trigger var1)
    • 作用:在Trigger错过触发时触发。
    • 参数:
      • var1:触发该事件的Trigger对象。
    • 用途:处理Trigger未能在预定时间触发的情况,可以执行相应的补救措施或记录日志。
  5. triggerComplete(Trigger var1, JobExecutionContext var2, CompletedExecutionInstruction var3)
    • 作用:在与Trigger关联的作业执行完成时触发。
    • 参数:
      • var1:触发该事件的Trigger对象。
      • var2:当前JobExecutionContext对象,包含Job执行的上下文信息。
      • var3:作业执行完成后的指令(例如,是否应删除Trigger等)。
    • 用途:处理Job执行完成后的清理工作或记录日志。可以根据 var3 参数的值来决定是否需要采取进一步的操作。

代码示例和JobListener类似,这里我们就不再赘述了。

2.3.3-SchedulerListeners

具体的接口如下:

在这里插入图片描述

具体接口的作用就不再赘述了,其使用也和TriggerListener和JobListener一样的

  • 15
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值