本篇博客紧接着上一篇Quartz理论部分
七、使用Quartz
1、入门案例
创建项目(我是参加的是springboot项目),先导入依赖:
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz-jobs</artifactId>
<version>2.3.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.quartz-scheduler/quartz -->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.3.1</version>
</dependency>
编写HelloJob.java 任务类,实现Job接口,重写execute方法:
public class HelloJob implements Job {
@Override
public void execute(JobExecutionContext context) throws JobExecutionException{
Date date = new Date();
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-mm-dd HH:mm:ss");
String dateString = dateFormat.format(date);
System.out.println("名称:" + context.getJobDetail().getKey().getName());
System.out.println("任务组名称:" + context.getJobDetail().getKey().getGroup());
System.out.println("任务类:" + context.getJobDetail().getClass().getName());
System.out.println("运行job时间:" + dateString);
}
}
在项目main方法中写其余部分代码:
public class HelloSchedulerDemo {
public static void main(String []args) throws SchedulerException {
//1 调度器(Scheduler),从工厂获取调度的实例
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
//2 任务实例(JobDetail)
JobDetail jobDetail = JobBuilder.newJob(HelloJob.class)//加载任务类,与HelloJob绑定,要求HelloJob实现Job接口
.withIdentity("job1", "group1")//参数1:任务的名称(唯一实例) 参数2:任务组的名称
.build();
//3 触发器 Trigger
SimpleTrigger trigger = TriggerBuilder.newTrigger()
.withIdentity("trigger")
.startNow()//马上启动触发器
//.startAt(date)
.withSchedule(SimpleScheduleBuilder.simpleSchedule().repeatForever().withIntervalInSeconds(5))//永远间隔5s开始
.build();
/*System.out.println("名称:" + jobDetail.getKey().getName());
System.out.println("组的名称:" + jobDetail.getKey().getGroup());
System.out.println("任务类:" + jobDetail.getJobClass().getName());*/
// 让调度器关联任务和触发器,保证按照触发器定义的条件执行任务
scheduler.scheduleJob(jobDetail,trigger);
//启动
scheduler.start();
//结束
//scheduler.shutdown();
}
}
运行就可以了。
2、Job和JobDetail介绍
3、JobExecutionContext介绍
- 当Scheduler调用一个Job时,就回将JobExecutionContext传递给Job的execute()方法
- Job能通过JobExecutionContext对象访问到Quartz运行时候的环境、Job本身的详细数据信息
4、JobDataMap介绍
(1)使用Map获取
- 在进行任务调度的时,JobDataMap存储在JobExecutionContext中,易获取
- JobDataMap可以用来装载任何可序列化的对象,当job实例对象被执行时,这些参数对象会传递给它
- JobDataMap 实现了JDK的Map接口,还添加了非常方便的方法用来存取基本数据类型
修改代码:
public class HelloJob implements Job {
@Override
public void execute(JobExecutionContext context) throws JobExecutionException{
Date date = new Date();
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-mm-dd HH:mm:ss");
String dateString = dateFormat.format(date);
System.out.println("名称:" + context.getJobDetail().getKey().getName());
System.out.println("任务组名称:" + context.getJobDetail().getKey().getGroup());
System.out.println("任务类:" + context.getJobDetail().getClass().getName());
//从JobDetail中获取JobDataMap参数信息
JobDataMap jobDataMap = context.getJobDetail().getJobDataMap();
String jobDataMapMessage = jobDataMap.getString("message");
System.out.println("任务数据的参数值:" + jobDataMapMessage);
//获取Trigger对象中获取JobDataMap的数据
JobDataMap triggerDataMap = context.getTrigger().getJobDataMap();
String triggerDataMapMessage = triggerDataMap.getString("message");
System.out.println("触发器数据的参数值:" + triggerDataMapMessage);
//获取Trigger的内容
TriggerKey triggerKey = context.getTrigger().getKey();
System.out.println("触发器名称:" + triggerKey.getName() + " 触发器组:" + triggerKey.getGroup());
//获取其他内容
System.out.println("当前任务的执行时间:" + dateFormat.format(context.getFireTime()));
System.out.println("下一次执行任务时间:" + dateFormat.format(context.getNextFireTime()));
//工作job内容
System.out.println("运行job工作内容:" + dateString);
}
}
public class HelloSchedulerDemo {
public static void main(String []args) throws SchedulerException {
//1 调度器(Scheduler),从工厂获取调度的实例
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
//2 任务实例(JobDetail)
JobDetail jobDetail = JobBuilder.newJob(HelloJob.class)//加载任务类,与HelloJob绑定,要求HelloJob实现Job接口
.withIdentity("job1", "group1")//参数1:任务的名称(唯一实例) 参数2:任务组的名称
.usingJobData("message","打印日志")//传递参数,名称message
.build();
//3 触发器 Trigger
SimpleTrigger trigger = TriggerBuilder.newTrigger()
.withIdentity("trigger")
.startNow()//马上启动触发器
//.startAt(date)
.withSchedule(SimpleScheduleBuilder.simpleSchedule().repeatForever().withIntervalInSeconds(5))//永远间隔5s开始
.usingJobData("message","simple触发器") //传递参数,名称message
.build();
/*System.out.println("名称:" + jobDetail.getKey().getName());
System.out.println("组的名称:" + jobDetail.getKey().getGroup());
System.out.println("任务类:" + jobDetail.getJobClass().getName());*/
// 让调度器关联任务和触发器,保证按照触发器定义的条件执行任务
scheduler.scheduleJob(jobDetail,trigger);
//启动
scheduler.start();
//结束
//scheduler.shutdown();
}
}
(2)Job实现类中添加setter方法对应的JobDataMap的键值,Quartz框架默认的JobFactory实现类在初始化job实例对象时,会自动调用这些setter方法
HelloJob.java中添加以下代码:
private String message;
public void setMessage(String message) {
this.message = message;
}
//从setter方法获得message的值
System.out.println("参数值:" + message);
**注意:**如果遇到同名的key,Trigger中的 .usingJobData(“message”,“simple触发器”)会覆盖JobDetail中的 .usingJobData(“message”,“打印日志”)
5、有状态的Job和无状态的Job
@PersistJobDataAfterExecution 注解的使用
有状态的Job可以理解为多次job调用期间可以持有一些状态信息,这些状态信息存储在JobDataMap中,默认的无状态的job每次调用时都会创建一个新的JobDataMap
(1)修改HelloSchedulerDemo.java
添加 .usingJobData(“count”,0),表示计数器
JobDetail jobDetail = JobBuilder.newJob(HelloJob.class)//加载任务类,与HelloJob绑定,要求HelloJob实现Job接口
.withIdentity("job1", "group1")//参数1:任务的名称(唯一实例) 参数2:任务组的名称
.usingJobData("message","打印日志")//传递参数,名称message
.usingJobData("count", 0)
.build();
(2) 修改HelloJob.java
添加以下代码:
private Integer count;
public void setCount(Integer count) {
this.count = count;
}
//输出count
++count;//累加count
//将count存放到JobDataMap中
context.getJobDetail().getJobDataMap().put("count",count );
此时运行结果,count值都是为1,不会累加,此时job是无状态的
添加注解@PersistJobDataAfterExecution ,此job变为有状态的job
@PersistJobDataAfterExecution // 多次调用Job在时候,会对Job进行持久化,保存一个数据的信息
public class HelloJob implements Job {
...
运行得到新结果,count可以实现累加
6、Trigger介绍
Quartz有一些不同的触发器类型,不过,用得最多的是SimpleTrigger和CronTrigger
(1)jobKey
表示job实例的标识,触发器被触发时,该指定的job实例会被执行
(2)startTime
表示触发的时间表,第一次开始被触发的时间,它的数据类型是java.util.Date
(3)endTime
指定触发器被触发的时间,它的数据类型是java.util.Date
案例:
MyJob.java
public class MyJob implements Job {
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
Date date = new Date();
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dateString = dateFormat.format(date);
//工作job内容
System.out.println("运行job工作内容:" + dateString);
//获取jobKey startTime endTime
Trigger trigger = context.getTrigger();
System.out.println("jobKey的名称:" + trigger.getJobKey().getName() +
" jobKey的组名称: " + trigger.getJobKey().getGroup());
System.out.println("任务开始时间:" + dateFormat.format(trigger.getStartTime()) +
" 任务结束时间:" + dateFormat.format(trigger.getEndTime()));
}
}
DemoApplication类
@SpringBootApplication
public class DemoApplication {
public static void main(String []args) throws SchedulerException {
//1 调度器(Scheduler),从工厂获取调度的实例
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
//设置任务开始时间
Date startDate = new Date();
startDate.setTime(startDate.getTime() + 3000);
//设置任务结束时间
Date endDate = new Date();
endDate.setTime(endDate.getTime()+10000);
//2 任务实例(JobDetail)
JobDetail jobDetail = JobBuilder.newJob(MyJob.class)//加载任务类,与HelloJob绑定,要求HelloJob实现Job接口
.withIdentity("job1", "group1")//参数1:任务的名称(唯一实例) 参数2:任务组的名称
.build();
//3 触发器 Trigger
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("trigger")
.startAt(startDate)
.endAt(endDate)
.withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(3).withRepeatCount(2)) //间隔3s开始重复执行2遍
.build();
// 让调度器关联任务和触发器,保证按照触发器定义的条件执行任务
scheduler.scheduleJob(jobDetail,trigger);
//启动
scheduler.start();
//结束
//scheduler.shutdown();
}
}
7、SimpleTrigger触发器
SimpleTrigger的设置和使用是最简单的一种QuartzTrigger,它是为那种需要在特定的日期/时间启动、以一个可能的时间间隔重复执行n次的Job所设计的
注意:
- SimpleTrigger的属性有:开始时间、结束时间、重复次数、重复的时间间隔
- 重复次数属性的值可以为0、正整数、或常量SimpleTrigger.REPEAT_INDEFINITELY(反复执行)
- 重复的时间间隔属性必须大于0或长整型的正整数,以毫秒作为时间单位,当重复的时间间隔为0时,意味着与Trigger同时触发进行
- 如果有指定结束时间属性,则结束时间属性优先于重复次数属性,好处:当我们需要重复创建一个每隔10秒触发一次指定的结束时间的Trigger,而无需去计算从开始到结束的重复次数,我们只需简单的执行结束时间和使用SimpleTrigger.REPEAT_INDEFINITELY作为重复次数的属性
10、CronTrigger触发器
如果需要像日历那样按日程来触发任务,而不是像SimpleTrigger那样每隔特定的间隔时间触发,CronTrigger通常比SimpleTrigger更有用,因为CronTrigger是基于日历的作业调度器
使用CronTrigger,可以指定例如“每个周五中午”、“每个工作日的9:30”、“从每个周一、周二的上午9:00到10:00之间每间隔5分钟”等日程安排来触发,甚至,先SimpleTrigger一样,CronTrigger也有一个startTime、endTime
官网案例:
http://www.quartz-scheduler.org/documentation/2.3.1-SNAPSHOT/examples/Example3.html
(1)Cron Expressions --Cron 表达式
Cron 表达式被用来配置CronTrigger实例。Cron表达式是一个由7个子表达式组成的字符串,每个子表达式都描述了一个单独的日程细节,用空格隔开,分别表示为
- Seconds 秒 0-59 可设置多个,用符号表示:, - * /
- Minutes 分钟 0-59 用符号表示:, - * /
- Hours 小时 0-23
- Day-of-Month 月中的天 1-31
- Month 月 1-27或者JAN-DEC 用符号表示:, - * /
- Day-of-Week 周中的天 1-7或者SUN-SAT
- Year(optional field)年(可选) 用符号表示:, - * /
具体表达式怎么写,可以使用在线Cron表达工具生成http://cron.qqe2.com/
SchedulerFactory sf = new StdSchedulerFactory();
Scheduler sched = sf.getScheduler();
JobDetail job = newJob(SimpleJob.class)
.withIdentity("job1", "group1")
.build();
CronTrigger trigger = newTrigger()
.withIdentity("trigger1", "group1")
.withSchedule(cronSchedule("0/20 * * * * ?"))
.build();
sched.scheduleJob(job, trigger);
sched.start();
11、配置、资源SchedulerFactory
一般是一个job一个触发器,如果需要一个更加复杂的触发计划,可以创建多个Trigger并指派它们给同一个job
Scheduler的创建方式
(1)StdSchedulerFactory(默认的SchedulerFactory)
- 使用一组参数(java.util.properties)来创建和初始化Quartz调度器
- 配置参数一般存储在quartz.properties文件中
- 调用getScheduler方法就能创建和初始化调度器对象