💠

  1. Quartz学习
    1. 官方样例
    2. Quartz中常用的一些类
    3. 有状态的Job与无状态的Job
    4. Quartz中重要的几个监听器

💠 2023-10-03 20:22


Quartz学习

添加依赖

1
2
    compile group: 'org.quartz-scheduler', name: 'quartz', version: '2.2.1'
    compile group: 'org.quartz-scheduler', name: 'quartz-jobs', version: '2.2.1'

官方样例

  • 定义需执行的任务类
1
2
3
4
5
6
7
8
/**
* 类实现Job接口中的execute方法
*/
class HelloJob: Job {
    override fun execute(context: JobExecutionContext?) {
        println("hello")
    }
}
  • 设置运行任务
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
fun main(args: Array<String>) {
    // 获取Scheduler用于调度任务
    var scheduler = StdSchedulerFactory.getDefaultScheduler()
    // 开始执行任务
    scheduler.start()

    // 定义需执行的任务,指定我们定义的任务类
    val job = newJob(HelloJob::class.java)
            .withIdentity("job1", "group1")
            .build()

    // 定义任务的触发器
    val trigger = newTrigger()
            .withIdentity("trigger1", "group1")
            .startNow()
            .withSchedule(simpleSchedule()
                    .withIntervalInSeconds(40)
                    .repeatForever())
            .build()

    // 告知Scheduler执行的任务及任务的触发器
    scheduler.scheduleJob(job, trigger)
    // 留点时间等待任务执行
    Thread.sleep(6000)
    // 关闭Scheduler
    scheduler.shutdown()
}

在官方示例中,通过类通过实现Job接口来定义任务。
在Quartz中,任务的运行需要定义任务与触发器;任务用于指定需任务,触发器用于定义任务的触发时机

Quartz中常用的一些类

  • Job: Job接口,任务类需要实现的接口。注意:实现该接口的类必须存在默认的无参构造器

  • JobDetail: 该类为Job提供了许多属性, 其中包含JobDataMap;Quartz不存储Job类的实际实例,而是通过使用JobDetail定义一个实例。JobDetails通过JobBuilder创建定义。

  • JobExecutionContext: 在实现Job接口的类中,实现的execute方法会传入JobExecutionContext的实例。调用的Job通过JobExecutionContext的实例可以访问到Quartz运行的环境及Job的一些信息等

  • JobDataMap: 可用于装载任何可序列化的数据。JobDataMap将存储在JobExecutionContext中,任务可通过JobExecutionContext实例来获取JobDataMap中的数据;(JobDataMap底层采用Map的数据结构),如:

    • 通过JobDataMap传递数据给Job
    1
    2
    3
    4
    5
    
    // 简单的传递键为hello,值为world的数据,该数据将会存放在JobDataMap中
    val job = newJob(HelloJob::class.java)
                .withIdentity("job1", "group1")
                .usingJobData("hello", "world")
                .build()
    
    • Job通过JobExecutionContext获取JobDataMap中的数据
    1
    2
    3
    4
    5
    6
    7
    8
    
    class HelloJob: Job {
        override fun execute(context: JobExecutionContext?) {
            val name = context!!.jobDetail.key.name
            // 获取JobDataMap中的数据,取值也可在Job类中定义与键对应的属性,这样也可获取数据
            val hello = context.jobDetail.jobDataMap["hello"]
            println("$name is running, $hello")
        }
    }
    
  • Trigger: 任务的触发器,在此处只介绍常用的两种触发器

    • SimpleTrigger: 它只能用于指定任务在一个特定时间内运行,可指定任务的重复(时间,次数)与间隔(时间,次数), 示例如下(简单介绍,具体请查看API)

      • 立即运行并每1秒运行一次,直到程序结束
      1
      2
      3
      4
      5
      6
      7
      
      val trigger = newTrigger()
                  .withIdentity("trigger1", "group1")
                  .startNow()
                  .withSchedule(simpleSchedule()
                          .withIntervalInSeconds(1)
                          .repeatForever())
                  .build()
      
      • 立即运行任务并每1秒运行一次,总共运行3次
      1
      2
      3
      4
      5
      6
      7
      
      val trigger = newTrigger()
                  .withIdentity("trigger1", "group1")
                  .startNow()
                  .withSchedule(simpleSchedule()
                          .withIntervalInSeconds(1)
                          .withRepeatCount(2))
                  .build()
      
    • CronTrigger: 该触发器可通过cron表达式来定义触发任务(不了解cron表达式的可百度,此处不做介绍)

      • 每秒执行一次任务
      1
      2
      3
      4
      5
      
      val trigger = newTrigger()
                  .withIdentity("trigger1", "group1")
                  .startNow()
                  .withSchedule(CronScheduleBuilder.cronSchedule("0/1 * * * * ?"))
                  .build()
      

有状态的Job与无状态的Job

在Quartz中,Job可能会持有某些状态信息,例如在Job中的index属性用于计算任务的调用次数,这些信息将被存储在JobDataMap,这时候便就是有状态Job
无状态Job在每次运行时都会创建新的JobDataMap,即同一任务的后一次调用无法获取前一次调用保存的信息

  • 将设置Job为有状态

若需将Job设置为有状态,只需在Job类上使用@PersistJobDataAfterExecution注解,这样任务在多次运行时则不会重新创建新的JobDataMap

  • 示例

    • 传递index值给Job
    1
    2
    3
    4
    
    val job = newJob(HelloJob::class.java)
                .withIdentity("job1", "group1")
                .usingJobData("index", 0)
                .build()
    
    • 将Job设置为有状态,并将index的值放入JobDataMap中(该示例通过在任务类中定义属性来获取JobDataMap的数据)
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    
    @PersistJobDataAfterExecution
    class HelloJob: Job {
    
        var index = 0
    
        override fun execute(context: JobExecutionContext?) {
            val name = context!!.jobDetail.key.name
            println("$name is running, index is $index")
            index++
            // 存放数据到JobDataMap中
            context.jobDetail.jobDataMap["index"] = index
        }
    }
    

Quartz中重要的几个监听器

  • JobListener: 任务调度中,与任务Job相关的监听器

    • 定义JobListener(通过实现JobListener接口)
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    
    class MyJobListener : JobListener {
        override fun getName(): String? {
            println("JobListener getName")
            return this.javaClass.name
        }
    
        override fun jobToBeExecuted(context: JobExecutionContext?) {
            println("${context!!.jobDetail.key.name} : 在Job将要被执行时执行")
        }
    
        override fun jobWasExecuted(context: JobExecutionContext?, jobException: JobExecutionException?) {
            println("${context!!.jobDetail.key.name} : 在Job被执行后执行")
        }
    
        override fun jobExecutionVetoed(context: JobExecutionContext?) {
            println("${context!!.jobDetail.key.name} : 在Job将要被执行时执行, 但又被TriggerListener否决时调用")
        }
    
    }
    
    • 将JobListener与任务调度关联

      • 定义全局JobListener
      1
      2
      3
      
      scheduler.scheduleJob(job, trigger)
      // 定义全局JobListener
      scheduler.listenerManager.addJobListener(MyJobListener(), EverythingMatcher.allJobs())
      
      • 定义局部JobListener
      1
      2
      3
      
      scheduler.scheduleJob(job, trigger)
      // 将JobListener与指定的任务Job相关联
      scheduler.listenerManager.addJobListener(MyJobListener(), KeyMatcher.keyEquals(JobKey.jobKey("job1", "group1")))
      
  • TriggerListener: 用于监听与Trigger相关的事件

    • 定义TriggerListener(实现TriggerListener接口)
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    
    class MyTriggerListener : TriggerListener {
        override fun triggerFired(trigger: Trigger?, context: JobExecutionContext?) {
            println("相关联的Trigger被触发,Job的execute将被执行时触发")
        }
    
        override fun getName(): String {
            println("MyTriggerListener : getName")
            return this::class.java.simpleName
        }
    
        override fun vetoJobExecution(trigger: Trigger?, context: JobExecutionContext?): Boolean {
            println("相关联的Trigger被触发,Job将被调用时,Scheduler调用该方法,可否决Job的执行,若返回true则该Job不会因此次Trigger触发而执行")
            return false
        }
    
        override fun triggerComplete(trigger: Trigger?, context: JobExecutionContext?, triggerInstructionCode: Trigger.CompletedExecutionInstruction?) {
            println("相关联的Trigger被触发,并且完成Job调用时执行")
        }
    
        override fun triggerMisfired(trigger: Trigger?) {
            println("当Trigger错过触发时调用")
        }
    }
    
    • 将TriggerListener与任务调度关联

      • 定义全局TriggerListener
      1
      2
      
      // 定义全局TriggerListener
      scheduler.listenerManager.addTriggerListener(MyTriggerListener(), EverythingMatcher.allTriggers())
      
      • 定义局部TriggerListener
      1
      2
      
      // 将riggerListener与指定的Trigger关联
      scheduler.listenerManager.addTriggerListener(MyTriggerListener(), KeyMatcher.keyEquals(TriggerKey.triggerKey("trigger1", "group1")))
      
  • SchedulerListener: 在Scheduler生命周期中的关键事件发生时调用