quartz学习笔记之相关概念整理

Quartz三个核心概念

  • 调度器:Scheduler
  • 任务:Job
  • 触发器:Trigger

Quartz体系结构

  • JobDetail:包含任务的实现类以及类的信息;
  • Trigger:决定任务什么时候被调用;分为SimpleTrigger和CronTrigger(常用);
  • scheduler:定时、周期地执行JobDetail的信息

Quartz重要组成

  • Job: 接口,只有一个参数;
  • JobDetail:Job的实现类;
  • JobBuilder:创建JobDetail的实例;
  • JobStore:保存Job数据;(保存到内存中、数据库中);
  • Trigger:
  • TreadPool:
  • TriggerBuilder:
  • Scheduler:调度器;
  • Calender:一个Trigger可以和多个Calender关联,以排除或者包含某些时间点
  • 监听器:JobListener、TriggerListener、SchedulerListener;

代码示例(一)

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
28
29
30
31
32
33
package com.weyoung.platform.quartz.schedule;

import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

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

/**
* @Author Mr.wang
* @Description
* @Date 2019/6/2
*/
public class QuartzJobInit implements Job {

private static final Logger LOGGER = LoggerFactory.getLogger(QuartzJobInit.class);

@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {

Date date = new Date();
SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String format = sf.format(date);
run(format);
}

public void run(String format) {
LOGGER.info("QuartzJobInit.execute.run:{}", format);
System.out.println("QuartzJobInit.execute.run:{}" + format);
}
}
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
28
29
30
31
32
33
34
35
36
37
38
package com.weyoung.platform.quartz.schedule;

import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;

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

/**
* @功能描述:
* @时间: 2019/6/22 23:43
* @作者: wanglixia
*/
public class TestJob {
public static void main(String[] args) {
// 创建一个JobDetail实例,将该实例与QuartzJobInit的Class进行绑定
JobDetail jobDetail = JobBuilder.newJob(QuartzJobInit.class).withIdentity("myJob", "group1").build();
// 创建一个Trigger实例,用来触发Job去执行
Trigger trigger = TriggerBuilder.newTrigger().withIdentity("myTrigger", "triggerGroup1")
.startNow()
.withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(2).repeatForever()).build();
// 创建Scheduler实例,
StdSchedulerFactory factory = new StdSchedulerFactory();
Scheduler scheduler = null;
try {
scheduler = factory.getScheduler();
scheduler.start();
Date date = new Date();
SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String format = sf.format(date);
System.out.println("QuartzJobInit.main" + format);
// LOGGER.info("QuartzJobInit.main", format);
scheduler.scheduleJob(jobDetail, trigger);
} catch (SchedulerException e) {
e.printStackTrace();
}
}
}

Job 接口的实现

  • Job接口中只有一个execute方法,然后在其中写业务逻辑;
    • 生命周期:每次调度器Scheduler在执行job时,会在调用execute方法之前创建一个新的job实例;调用完成后,关联的job对象实例会被释放,释放的实例会被垃圾回收机制回收。
  • JobDetail为Job实例提供了许多设置属性,以及JobDataMap成员变量属性,用来存储特定Job实例的状态信息,调度器需要借助JobDetail对象来添加Job实例。重要属性如下:
    • name
    • group 任务所在组
    • jobClass 任务实现类
    • jobDataMap

JobExecutionContext

  • 当Scheduler调用一个Job时,就会将JobExecutionContext传递给Job的execute方法;
  • Job能通过JobExecutionContext对象访问到Quartz运行时候的环境以及Job本身的明细数据。

JobDataMap

  • 在进行任务调度时JobDataMap存储在JobExecutionContext中,非常方便获取;
  • JobDataMap可以用来装载任何可序列化的数据对象,当job实例对象被执行时这些参数对象会传递给它;
  • JobDataMap实现了JDK的Map接口,并且添加了一些非常方便的方法用来存取基本数据类型。

JobDataMap获取方式:

从Map中直接获取;
  • 方式一:JobDataMap

添加数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 创建一个JobDetail实例,将该实例与QuartzJobInit的Class进行绑定
JobDetail jobDetail = JobBuilder.newJob(QuartzJobInit.class)
.withIdentity("myJob", "group1")
// 传参
.usingJobData("message", "hello myJob")
.usingJobData("FloatJobValue", 3.14F)
.build();
// 创建一个Trigger实例,用来触发Job去执行
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("myTrigger", "triggerGroup1")
.usingJobData("message", "hello myTrigger")
.usingJobData("DoubleTriggerValue", 2.0D)
.startNow()
.withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(2).repeatForever()).build();

获取数据:

1
2
3
4
5
6
7
8
9
10
11
JobKey key = context.getJobDetail().getKey();
System.out.println("key============>" + key.getName()+":" + key.getGroup());
TriggerKey triggerKey = context.getTrigger().getKey();
System.out.println("triggerKey============>" + triggerKey.getName()+":" + triggerKey.getGroup());
JobDataMap jobDataMap = context.getJobDetail().getJobDataMap();
JobDataMap dataMap = context.getTrigger().getJobDataMap();

String message = jobDataMap.getString("message");
float floatJobValue = jobDataMap.getFloatValue("FloatJobValue");
String triggerMsg = dataMap.getString("message");
double doubleJobValue = dataMap.getDoubleValue("DoubleTriggerValue");
  • 方式二:getMergedJobDataMap
    • Trigger和JobDetail中有相同的key时,Trigger中的值就会覆盖掉JobDetail中的值。
    • Job实现类中添加setter方法对应JobDataMap的键值(Quartz框架默认的JobFactory实现类在初始化job实例对象时会自动调用这些setter方法)。
    • 直接定义属性,添加getter和setter方法即可;
      1
      JobDataMap mergedJobDataMap = context.getMergedJobDataMap();

触发器 Trigger

重要的两个实现类

  • CronTriggerImpl
    • 基于日历的作业调度器,而不是像SimpleTrigger那样精确指定间隔时间,比SimpleTrigger更常用;
    • CRON表达式:L可以和W一起使用;周字段的英文字母不区分大小写;
  • SimpleTriggerImpl
    • 在一个指定时间段内执行一次作业任务;或者在指定时间间隔内多次执行作业任务;
    • 重复次数可以为0,正整数或是SimpleTrigger.REPEAT_INDEFINITELY常量值。
    • 重复时间间隔必须是0或者长整数;
    • 一旦制定了endAt,那么它就会覆盖重复次数参数的效果。endAt优先于withRepeatCount;

重要属性

  • JobKey:job实例的标识,触发器被触发时,该jobkey指定的实例会被触发执行;
  • StartTime:触发器首次被触发的时间;类型java.util.Date;
  • EndTime:触发器不再被触发的时间;类型java.util.Date;

调度器Scheduler

Scheduler-工厂模式

  • SchedulerFactory创建的;
    • StdSchedulerFactory;
      • 使用一组参数(java.util.Properties)来创建和初始化Quartz调度器;
      • 配置参数一般在quartz.properties中
      • 调用getScheduler方法就能创建和初始化调度器对象;
    • DirectSchedulerFactory;

主要函数

  • Date scheduleJob(JobDetail jobDetail, Trigger trigger);绑定jobDetail和trigger注册进scheduler中;返回最近一次即将执行的时间;
  • void start(); // 启动
  • void standby();// 挂起,挂起后可通过start恢复
  • void shutdown();// 关闭,不能恢复执行,强行恢复报出异常
    • shutdown(true):// 等待所有正在执行的job执行完毕之后,再关闭scheduler;
    • shutdown(false):// 表示直接关闭scheduler

quartz.properties

  • 文档的位置及加载顺序;优先读取工程中自定义的,没有的话去读quartz中的文件;
  • 组成部分
    • 调度器属性
      • org.quartz.scheduler.instanceName:用来区分特定的调度器实例,可以按照功能用途来给调度器起名;
      • org.quartz.scheduler.instanceId:和前者一样,也允许任何字符串,但这个值必须在所有调度器实例中是唯一的,尤其是在一个集群当中,作为集群的唯一key。如果想让Quartz生成这个值的话,可以设置为AUTO;
    • 线程池属性
      • threadCount:指定线程数,至少为1;
      • threadPriority:线程的优先级;
      • org.quartz.threadPool.class:线程池的实现类;
    • 作业存储设置
      • 描述了在调度器实例的生命周期中,Job和Trigger的信息是如何被存储的;
    • 插件配置
      • 满足特定需求用到的Quartz插件的配置。

详细配置如下:

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# Default Properties file for use by StdSchedulerFactory
# to create a Quartz Scheduler Instance, if a different
# properties file is not explicitly specified.
#
# ===========================================================================
# Configure Main Scheduler Properties 调度器属性
# ===========================================================================
org.quartz.scheduler.instanceName: DefaultQuartzScheduler
org.quartz.scheduler.instanceid:AUTO
org.quartz.scheduler.rmi.export: false
org.quartz.scheduler.rmi.proxy: false
org.quartz.scheduler.wrapJobExecutionInUserTransaction: false
# ===========================================================================
# Configure ThreadPool 线程池属性
# ===========================================================================
#线程池的实现类(一般使用SimpleThreadPool即可满足几乎所有用户的需求)
org.quartz.threadPool.class: org.quartz.simpl.SimpleThreadPool
#指定线程数,至少为1(无默认值)(一般设置为1-100直接的整数合适)
org.quartz.threadPool.threadCount: 10
#设置线程的优先级(最大为java.lang.Thread.MAX_PRIORITY 10,最小为Thread.MIN_PRIORITY 1,默认为5)
org.quartz.threadPool.threadPriority: 5
#设置SimpleThreadPool的一些属性
#设置是否为守护线程
#org.quartz.threadpool.makethreadsdaemons = false
#org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread: true
#org.quartz.threadpool.threadsinheritgroupofinitializingthread=false
#线程前缀默认值是:[Scheduler Name]_Worker
#org.quartz.threadpool.threadnameprefix=swhJobThead;
# 配置全局监听(TriggerListener,JobListener) 则应用程序可以接收和执行 预定的事件通知
# ===========================================================================
# Configuring a Global TriggerListener 配置全局的Trigger监听器
# MyTriggerListenerClass 类必须有一个无参数的构造函数,和 属性的set方法,目前2.2.x只支持原始数据类型的值(包括字符串)
# ===========================================================================
#org.quartz.triggerListener.NAME.class = com.swh.MyTriggerListenerClass
#org.quartz.triggerListener.NAME.propName = propValue
#org.quartz.triggerListener.NAME.prop2Name = prop2Value
# ===========================================================================
# Configuring a Global JobListener 配置全局的Job监听器
# MyJobListenerClass 类必须有一个无参数的构造函数,和 属性的set方法,目前2.2.x只支持原始数据类型的值(包括字符串)
# ===========================================================================
#org.quartz.jobListener.NAME.class = com.swh.MyJobListenerClass
#org.quartz.jobListener.NAME.propName = propValue
#org.quartz.jobListener.NAME.prop2Name = prop2Value
# ===========================================================================
# Configure JobStore 存储调度信息(工作,触发器和日历等)
# ===========================================================================
# 信息保存时间 默认值60秒
org.quartz.jobStore.misfireThreshold: 60000
#保存job和Trigger的状态信息到内存中的类
org.quartz.jobStore.class: org.quartz.simpl.RAMJobStore
# ===========================================================================
# Configure SchedulerPlugins 插件属性 配置
# ===========================================================================
# 自定义插件
#org.quartz.plugin.NAME.class = com.swh.MyPluginClass
#org.quartz.plugin.NAME.propName = propValue
#org.quartz.plugin.NAME.prop2Name = prop2Value
#配置trigger执行历史日志(可以看到类的文档和参数列表)
org.quartz.plugin.triggHistory.class = org.quartz.plugins.history.LoggingTriggerHistoryPlugin
org.quartz.plugin.triggHistory.triggerFiredMessage = Trigger {1}.{0} fired job {6}.{5} at: {4, date, HH:mm:ss MM/dd/yyyy}
org.quartz.plugin.triggHistory.triggerCompleteMessage = Trigger {1}.{0} completed firing job {6}.{5} at {4, date, HH:mm:ss MM/dd/yyyy} with resulting trigger instruction code: {9}
#配置job调度插件 quartz_jobs(jobs and triggers内容)的XML文档
#加载 Job 和 Trigger 信息的类 (1.8之前用:org.quartz.plugins.xml.JobInitializationPlugin)
org.quartz.plugin.jobInitializer.class = org.quartz.plugins.xml.XMLSchedulingDataProcessorPlugin
#指定存放调度器(Job 和 Trigger)信息的xml文件,默认是classpath下quartz_jobs.xml
org.quartz.plugin.jobInitializer.fileNames = my_quartz_job2.xml
#org.quartz.plugin.jobInitializer.overWriteExistingJobs = false
org.quartz.plugin.jobInitializer.failOnFileNotFound = true
#自动扫描任务单并发现改动的时间间隔,单位为秒
org.quartz.plugin.jobInitializer.scanInterval = 10
#覆盖任务调度器中同名的jobDetail,避免只修改了CronExpression所造成的不能重新生效情况
org.quartz.plugin.jobInitializer.wrapInUserTransaction = false
# ===========================================================================
# Sample configuration of ShutdownHookPlugin ShutdownHookPlugin插件的配置样例
# ===========================================================================
#org.quartz.plugin.shutdownhook.class = \org.quartz.plugins.management.ShutdownHookPlugin
#org.quartz.plugin.shutdownhook.cleanShutdown = true
#
# Configure RMI Settings 远程服务调用配置
#
#如果你想quartz-scheduler出口本身通过RMI作为服务器,然后设置“出口”标志true(默认值为false)。
#org.quartz.scheduler.rmi.export = false
#主机上rmi注册表(默认值localhost)
#org.quartz.scheduler.rmi.registryhost = localhost
#注册监听端口号(默认值1099)
#org.quartz.scheduler.rmi.registryport = 1099
#创建rmi注册,false/never:如果你已经有一个在运行或不想进行创建注册
# true/as_needed:第一次尝试使用现有的注册,然后再回来进行创建
# always:先进行创建一个注册,然后再使用回来使用注册
#org.quartz.scheduler.rmi.createregistry = never
#Quartz Scheduler服务端端口,默认是随机分配RMI注册表
#org.quartz.scheduler.rmi.serverport = 1098
#true:链接远程服务调度(客户端),这个也要指定registryhost和registryport,默认为false
# 如果export和proxy同时指定为true,则export的设置将被忽略
#org.quartz.scheduler.rmi.proxy = false

基于Maven的SpringMvc工程整合Quartz

使用Quartz配置作业

  • MethodInvokingJobDetailFactoryBean;
  • JobDetailFactoryBean:比较灵活,支持给作业传递数据;

Quartz相关文档

相关链接

本文整理自:慕课网教程
Java定时任务调度工具详解之Quartz篇:https://www.imooc.com/learn/846