Quartz第一天内容
Quartz官方网站:http://www.quartz-scheduler.org
什么是Quartz?
- Quartz是一个功能非常强大的开源的作业调度框架;
- 几乎可以集成任何java应用,小到单体应用,达到大型电子商务系统;
- 可以用来执行成千上万的简单或者复杂的调度任务;
- Quartz的任务被定义为一组标准的java组件,几乎可以执行任何编程任务;
- Quartz包含了一些企业级功能:包括对JTA事务支持和集群功能;
Quartz能做什么?
定时发送邮件、短信通知;
数据同步;
Quartz特点
强大的调度功能;
灵活的使用方式;
表达式 秒 分 时 天 月 周
0 0 8 ? 5 SUN#3
集群功能;
Quartz的重要API
Job接口:任务;定义的任务要实现Job接口;
JobDetail:对任务的描述;
Trigger:触发器,可以定义任务执行时机、执行规则
- SimpleTrigger
- CronTrigger
Scheduler:任务容器,调度器,使用触发器定义的规则去调度任务;
JobStore:内存、JDBC;
第一个Quartz程序:每隔5秒钟打印一次Hello Quartz!
引入Quartz依赖
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.3.0</version>
</dependency>
首先编写任务:HelloQuartz implements Job
HelloQuartzMain
- 创建JobDetail
- 创建Trigger
- 创建Scheduler
- 启动调度
package com.etoak.quartz01;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public class HelloQuartzJob implements Job {
public HelloQuartzJob() {
System.out.println("创建HelloQuartzJob对象");
}
@Override
public void execute(JobExecutionContext context) {
LocalDateTime dateTime = LocalDateTime.now();
String date = dateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss SSS"));
System.out.println(date + "-Hello Quartz!");
}
}
package com.etoak.quartz01;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
public class HelloQuartzMain {
public static void main(String[] args) throws SchedulerException {
// 1.创建JobDetail,使用建造者模式
JobDetail jobDetail = JobBuilder.newJob(HelloQuartzJob.class)
.withIdentity("hello", "hello")
.build();
// 2.创建Trigger
SimpleTrigger trigger = TriggerBuilder.newTrigger()
.withIdentity("hello", "hello")
.withSchedule(SimpleScheduleBuilder
.simpleSchedule()
.withIntervalInSeconds(5) // 每5秒钟执行一次
.repeatForever()
).build();
// 3.创建Scheduler
Scheduler scheduler = StdSchedulerFactory
.getDefaultScheduler();
scheduler.scheduleJob(jobDetail, trigger);
scheduler.start();
}
}
JobDetail和Trigger如何给Job任务传参、讲解SimpleTrigger
1. 任务两秒后执行、之后每隔三秒执行一次;
JobDetail给Job任务传参
usingJobData();
package com.etoak.quartz02;
import org.quartz.*;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public class JobDetailParamJob implements Job {
private Integer id;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
JobDetail jobDetail = context.getJobDetail();
JobDataMap dataMap = jobDetail.getJobDataMap();
String first = dataMap.getString("first");
String date = LocalDateTime.now()
.format(DateTimeFormatter
.ofPattern("yyyy-MM-dd HH:mm:ss"));
System.out.println(date + "-first:" + first);
System.out.println(date + "-second:" + id);
boolean third = dataMap.getBoolean("third");
System.out.println(date + "-third:" + third);
}
}
package com.etoak.quartz02;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import java.text.SimpleDateFormat;
import java.util.Date;
// 任务两秒后执行、之后每隔三秒执行一次;
// JobDetail给Job任务传参
public class JobDetailParamMain {
public static void main(String[] args) throws SchedulerException {
JobDataMap dataMap = new JobDataMap();
dataMap.put("third", true);
JobDetail jobDetail = JobBuilder.newJob(JobDetailParamJob.class)
.withIdentity("param")
.usingJobData("first", "第一种传参")
.usingJobData("id", 100)
.usingJobData(dataMap)
.build();
// 创建Trigger
Date current = new Date();
System.out.println( new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(current));
// 获得两秒后时间
Date twoSecondsAfter = new Date(current.getTime() + 2000L);
SimpleTrigger trigger = TriggerBuilder.newTrigger()
.withIdentity("param")
.startAt(twoSecondsAfter) //两秒后开始执行
.withSchedule(SimpleScheduleBuilder
.simpleSchedule()
.withIntervalInSeconds(3) //之后每隔3秒执行一次
.repeatForever()
).build();
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
scheduler.scheduleJob(jobDetail, trigger);
scheduler.start();
}
}
2. 任务两秒后执行,之后每隔三秒一次,15秒后停止执行;
Trigger给Job任务传参
usingJobData()
package com.etoak.quartz03;
import org.quartz.*;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public class TriggerParamJob implements Job {
private Integer second;
public Integer getSecond() {
return second;
}
public void setSecond(Integer second) {
this.second = second;
}
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
Trigger trigger = context.getTrigger();
JobDataMap dataMap = trigger.getJobDataMap();
String first = dataMap.getString("first");
boolean third = dataMap.getBoolean("third");
String date = LocalDateTime.now()
.format(DateTimeFormatter
.ofPattern("yyyy-MM-dd HH:mm:ss"));
System.out.println(date + "-first:" + first);
System.out.println(date + "-second:" + second);
System.out.println(date + "-third:" + third);
}
}
这里时间用的是LocalDateTime,传参的时候转了格式
Java 8时间接口localDateTime和Date的对比
package com.etoak.quartz03;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;
public class TriggerParamJobMain {
public static void main(String[] args) throws SchedulerException {
JobDetail jobDetail = JobBuilder.newJob(TriggerParamJob.class)
.withIdentity("param", "param")
.build();
LocalDateTime now = LocalDateTime.now();
System.out.println("当前时间:" + now);
LocalDateTime twoSecondsAfter = now.plusSeconds(2);
LocalDateTime fifteenSecondsAfter = now.plusSeconds(15);
Date two = Date.from(twoSecondsAfter.atZone(ZoneId.systemDefault()).toInstant());
Date fifteen = Date.from(fifteenSecondsAfter.atZone(ZoneId.systemDefault()).toInstant());
JobDataMap dataMap = new JobDataMap();
dataMap.put("third", false);
SimpleTrigger trigger = TriggerBuilder.newTrigger()
.withIdentity("param")
.startAt(two)
.endAt(fifteen)
.usingJobData("first", "第一种方式")
.usingJobData("second", 2)
.usingJobData(dataMap)
.withSchedule(SimpleScheduleBuilder.simpleSchedule()
.withIntervalInSeconds(3)
.repeatForever()
).build();
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
scheduler.scheduleJob(jobDetail, trigger);
scheduler.start();
}
}
3. 任务两秒后执行,之后每隔三秒执行一次,执行2次;
合并JobDetail和Trigger的参数
package com.etoak.quartz04;
import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Map;
import java.util.Set;
public class MergeParamJob implements Job {
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
JobDataMap dataMap = context.getMergedJobDataMap();
String date = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
Set<Map.Entry<String, Object>> entrySet = dataMap.entrySet();
for (Map.Entry<String, Object> entry : entrySet) {
String key = entry.getKey();
Object value = entry.getValue();
System.out.println(date + "-" + key + ':' + value);
}
}
}
package com.etoak.quartz04;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import java.text.SimpleDateFormat;
import java.util.Date;
public class MegerParamMain {
public static void main(String[] args) throws SchedulerException {
Date current = new Date();
System.out.println( new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(current));
// 获得两秒后时间
Date twoSecondsAfter = new Date(current.getTime() + 2000L);
SimpleTrigger trigger = TriggerBuilder.newTrigger()
.usingJobData("third", 3)
.usingJobData("second", "trigger")
.startAt(twoSecondsAfter)
.withSchedule(SimpleScheduleBuilder
.simpleSchedule().withIntervalInSeconds(3)
.withRepeatCount(2)
).build();
JobDetail jobDetail = JobBuilder.newJob(MergeParamJob.class)
.usingJobData("first", "first")
.usingJobData("second", "JobDetail")
.build();
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
scheduler.scheduleJob(jobDetail, trigger);
scheduler.start();
}
}
上面的两次指的是第一次执行之后再执行两次。
Cron表达式
Cron-Expressions用于配置CronTrigger的实例。
Cron Expressions是由七个子表达式组成的字符串,用于描述时间的各个细节。这些子表达式用空格分隔,并表示:
[秒] [分] [小时] [天] [月] [周] [年]
Seconds Minutes Hours Day-of-Month Month Day-of-Week Year (optional field)
时间字段 是否必填 允许值 特殊字符
秒 是 0-59 ,-*/
分 是 0-59 ,-*/
时 是 0-23 ,-*/
日 是 1-31 , - * ? / L W C
月 是 1-12或者JAN-DEC ,-*/
周 是 1-7或者SUN-SAT ,-* ? / L C # 说明:周天用1表示,依次类推
年 否 空或1970-2099 ,-*/
,:表示或的关系
-:范围的关系【比如1-21】
*:每秒、每分、每小时等
/:每天哪个时间执行
L:用于每月,或每周,表示为每月的最后一天,或每个月的最后星期几如“6L”表示“每月的最后一个星期五”
W:该字符只能出现在日期字段里,是对前导日期的修饰,表示离该日期最近的工作日。
例如15W表示离该月15号最近的工作日,如果该月15号是星期六,则匹配14号星期五;如果15日是星期日,则匹配16号星期一;
如果15号是星期二,那结果就是15号星期二。
但必须注意关联的匹配日期不能够跨月,如你指定1W,如果1号是星期六,结果匹配的是3号星期一,而非上个月最后的那天。
W字符串只能指定单一日期,而不能指定日期范围;
#:是用来指定“的”每月第n个工作日,例在每周(day-of-week)这个字段中内容为"6#3" or "FRI#3" 则表示“每月第三个星期五”
表达式举例
每天14:20提醒打卡:0 20 14 ? * MON-FRI
每天1点到1点59分,每隔5分执行:0 0/5 1 * * ?
周一到周五9:00上班的时候执行:0 0 9 ? * 2-6
注意点:?只能用在"天和周"
如果有一个字段是*,那么另外一个用?;
天和周有一个确定了,另外一个就得用?;
package com.etoak.quartz05;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class CronJob implements Job {
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
System.out.println(
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
.format(new Date()) + "-Hello Quartz"
);
}
}
package com.etoak.quartz05;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
public class CronJobMain {
public static void main(String[] args) throws SchedulerException {
// JobDetail
JobDetail jobDetail = JobBuilder.newJob(CronJob.class)
.withIdentity("cron", "cron")
.build();
// Trigger
CronTrigger trigger = TriggerBuilder
.newTrigger()
.withSchedule(CronScheduleBuilder
.cronSchedule("0/5 * * * * ?")
).build();
// Scheduler
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
scheduler.scheduleJob(jobDetail, trigger);
scheduler.start();
}
}
整合Spring
maven依赖
org.quartz-scheduler:quartz:2.3.0
org.springframework:spring-jdbc:5.0.7.RELEASE
org.springframework:spring-tx:5.0.7.RELEASE
org.springframework:spring-context-support:5.0.7.RELEASE
配置ioc容器中的bean
MethodInvokingJobDetailFactoryBean
特点:不要求任务类实现任何接口
属性:name、group、targetObject(job bean)、targetMethod(方法名)
CronTriggerFactoryBean
属性:name、group、jobDetail、cronExpression
SchedulerFactoryBean
属性:triggers --> list
<!-- MethodInvokingJobDetailFactory
不要求任务类实现Job接口
-->
<bean id="jobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="name" value="springJob" />
<property name="group" value="springJob" />
<property name="targetObject" ref="springJob" />
<property name="targetMethod" value="executeJob" />
</bean>
<!-- 任务类 -->
<bean id="springJob" class="com.etoak.job.SpringJob" />
<!-- CronTriggerFactoryBean -->
<bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
<property name="group" value="springJob" />
<property name="name" value="springJob" />
<property name="jobDetail" ref="jobDetail" />
<property name="cronExpression" value="0/5 * * ? * *" />
</bean>
<!-- SchedulerFactoryBean -->
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<ref bean="cronTrigger" />
</list>
</property>
</bean>
Quartz集群
1. 创建数据库和表
创建一个名称为quartz的数据库(字符集编码:utf8)
创建表:执行tables_mysql_innodb.sql
2. 创建项目,添加maven依赖
org.quartz-scheduler:quartz:2.3.0
org.springframework:spring-jdbc:5.0.7.RELEASE
org.springframework:spring-tx:5.0.7.RELEASE
org.springframework:spring-context-support:5.0.7.RELEASE
mysql:mysql-connector-java:5.1.34
3. 将quartz.properties文件放在src/main/resources下
在配置SchedulerFactoryBean的时候,有个configLocation属性,指定quartz.properties文件的位置;
#可以设置为任意,用在 JDBC JobStore中来唯一标识实例,但是所有集群节点中必须相同。
org.quartz.scheduler.instanceName=SpringClusterTest
# 属性为 AUTO即可,基于主机名和时间戳来产生实例 ID
org.quartz.scheduler.instanceId = AUTO
## 线程
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount = 10
org.quartz.threadPool.threadPriority = 5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true
## 存储
org.quartz.jobStore.misfireThreshold = 60000
#JobStoreTX,将任务持久化到数据库中。因为集群中节点依赖于数据库来传播 Scheduler实例的状态,你只能在使用 JDBC JobStore 时应用 Quartz 集群。这意味着你必须使用 JobStoreTX 或是 JobStoreCMT 作为 Job 存储,不能在集群中使用 RAMJobStore。
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.jobStore.tablePrefix = QRTZ_
org.quartz.jobStore.maxMisfiresToHandleAtATime=10
#值 true,表示 Scheduler实例要参与到一个集群当中。这一属性会贯穿于调度框架的始终,用于修改集群环境中操作的默认行为。
org.quartz.jobStore.isClustered = true
#定义了Scheduler 实例检入到数据库中的频率(单位:毫秒)。Scheduler 检查是否其他的实例到了它们应当检入的时候未检入;这能指出一个失败的 Scheduler 实例,且当前 Scheduler 会以此来接管任何执行失败并可恢复的 Job。通过检入操作,Scheduler 也会更新自身的状态记录。clusterChedkinInterval 越小,Scheduler 节点检查失败的 Scheduler 实例就越频繁。默认值是 15000 (即15 秒)。
org.quartz.jobStore.clusterCheckinInterval = 5000
4. 配置spring容器
DataSource(HikariDataDource)
属性:driverClassName、jdbcUrl、username、password
DataSourceTransactionManager
属性:dataSource
JobDetailFactoryBean
属性:name、group、jobClass、durability(持久化任务)
特点:要求任务继承QuartzJobBean类
CronTriggerFactoryBean
属性:name、group、jobDetail、cronExpression
SchedulerFactoryBean
属性:triggers、configLocation、dataSource、transactionManager、applicationContextSchedulerContextKey(spring ioc)