上次学习了Timer,我们知道了在项目中使用Timer有很多不足之处,那么对那些复杂的定时调度任务,我们应该怎么处理呢? 这个时候Quartz就闪亮登场啦,它会帮我们解决这些难题;
1, 首先我们来说一下 Quartz 与 Timer 的区别:
(1), 出身不同, Timer 为JDK自带; Quartz 为开源项目;
(2), 能力区别, Timer 远没有 Quartz 功能强大和完善, Timer 不能搞定的交给 Quartz ;
(3), 底层机制不同, Timer 利用后台子线程实现, Quartz 可以用于线程池中;
2, Quartz简介:
OpenSymphony提供的强大的开源任务调度框架;
官网: http://www.quartz-scheduler.org/
Maven仓库:
<!-- https://mvnrepository.com/artifact/org.quartz-scheduler/quartz -->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.3.0</version>
</dependency>
3, 好了,先来个HelloWorld:
(1),引入Jar包;(不用Maven的话需要引入: quartz-2.3.0.jar, slf4j-api-1.7.25.jar)
(2),创建任务:
package quartz;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
public class HelloJob implements Job{
@Override
public void execute(JobExecutionContext arg0) throws JobExecutionException {
Calendar calender = Calendar.getInstance();
SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// TODO 具体的业务逻辑:
System.out.println("Hellow Quartz! 执行时间:"+sf.format(calender.getTime()));
}
}
(3),调度任务:
package quartz;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.SimpleScheduleBuilder;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.impl.StdSchedulerFactory;
public class HelloScheduler {
public static void main(String[] args) throws SchedulerException {
Calendar calender = Calendar.getInstance();
SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// 创建一个 JobDetail 实例,将该实例与 HelloJob Class 绑定;
JobDetail jobDetail = JobBuilder.newJob(HelloJob.class)
.withIdentity("myJob").build();
// 创建一个Trigger实例,定义该job立即执行,并且每隔1秒钟重复执行一次;
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("myTrigger")
.startNow()
.withSchedule(SimpleScheduleBuilder.simpleSchedule()
.withIntervalInSeconds(1).repeatForever())
.build();
// 创建 Scheduler 实例;
SchedulerFactory sfact = new StdSchedulerFactory();
Scheduler scheduler = sfact.getScheduler();
scheduler.scheduleJob(jobDetail, trigger);
System.out.println("当前时间:"+sf.format(calender.getTime()));
scheduler.start();
}
}
4, 现在我们再来分析一下上面的例子:
(1), 首先是我们要创建一个Job: HelloJob; Job的应用及生命周期:
/**
* (1),Job接口非常容易实现,只有一个execute方法,类似TimerTask的run方法,在里面编写业务逻辑;
* (2),Job实例在Quartz中的生命周期:
* 每次调度器执行Job时,它在调用execute方法前会创建一个新的Job实例.当调用完成后,关联的Job对象实例会被释放,之后会被垃圾回收;
**/
public class HelloJob implements Job{
@Override
public void execute(JobExecutionContext arg0) throws JobExecutionException {
// TODO 具体的业务逻辑:
}
}
(2), 之后需要调用我们的HelloJob,那么就有了JobDetail:
public class Test{
public static void main(String[] args) throws SchedulerException {
/**
* 创建一个 JobDetail 实例,将该实例与 HelloJob Class 绑定;
*
* JobDetail为Job实例提供了许多设置属性,以及JobDataMap成员变量属性,
* 它用来储存特定Job实例的状态信息,调度器需要借助JobDetail对象来添加Job实例.
* 重要属性: name, group, jobClass, jobDataMap
* */
JobDetail jobDetail = JobBuilder.newJob(HelloJob.class)
.withIdentity("myJob","group1").build();
//我们可以通过 jobDetail.getKey() 来获取这些属性,方便以后调度;
System.out.println("jobDetail name is "+jobDetail.getKey().getName());
System.out.println("jobDetail group is "+jobDetail.getKey().getGroup());
System.out.println("jobDetail jobClass is "+jobDetail.getJobClass());
}
}
5, 通过 JobExecutionContext 来获取向 Job 中传入的参数:
(1), 传入参数:
package quartz;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.SimpleScheduleBuilder;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.impl.StdSchedulerFactory;
public class HelloScheduler {
public static void main(String[] args) throws SchedulerException {
Calendar calender = Calendar.getInstance();
SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
/**
* 创建一个 JobDetail 实例,将该实例与 HelloJob绑定;
*
* JobDetail为Job实例提供了许多设置属性,以及JobDataMap成员变量属性,它用来储存特定Job实例的状态信息,调度器需要借助JobDetail对象来添加Job实例.
* 重要属性: name, group, jobClass, jobDataMap
* */
JobDetail jobDetail = JobBuilder.newJob(HelloJob.class)
//传入JobDetail的属性及参数
.withIdentity("myJob","group1")
.usingJobData("message", "HelloJobDetail")
.usingJobData("FloatMessage", 2.2F)
.build();
//通过 getKey()获取相关属性;
System.out.println("jobDetail name is "+jobDetail.getKey().getName());
System.out.println("jobDetail group is "+jobDetail.getKey().getGroup());
System.out.println("jobDetail jobClass is "+jobDetail.getJobClass());
// 创建一个Trigger实例,定义该job立即执行,并且每隔1秒钟重复执行一次;
Trigger trigger = TriggerBuilder.newTrigger()
//传入Trigger(触发器)的属性及参数
.withIdentity("myTrigger")
.usingJobData("message", "HelloTrigger")
.usingJobData("DoubleMessage", 2.4D)
.startNow()
.withSchedule(SimpleScheduleBuilder.simpleSchedule()
.withIntervalInSeconds(1).repeatForever())
.build();
// 创建 Scheduler 实例;
SchedulerFactory sfact = new StdSchedulerFactory();
Scheduler scheduler = sfact.getScheduler();
scheduler.scheduleJob(jobDetail, trigger);
System.out.println("当前时间:"+sf.format(calender.getTime()));
scheduler.start();
}
}
(2), 获取参数:
package quartz;
import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.JobKey;
import org.quartz.TriggerKey;
public class HelloJob implements Job{
/**
* JobExecutionContext :
* (1), 当Scheduler(调度器) 调用一个Job,就会将JobExecutionContext传递给Job的execute()方法;
* (2), Job能通过JobExecutionContext对象访问到Quartz运行时候的环境及Job本身的明细数据;
**/
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
//获取传入的任务属性信息;
JobKey jobKey = context.getJobDetail().getKey();
System.out.println("Job(任务) : name: "+jobKey.getName()+", group: "+jobKey.getGroup());
//获取传入的触发器属性信息;
TriggerKey triggerKey = context.getTrigger().getKey();
System.out.println("Trigger(触发器) : name: "+triggerKey.getName()+", group: "+triggerKey.getGroup());
/**
* JobDataMap:
* (1), 在进行任务调度时JobDataMap存储,在JobExecutionContext中,非常方便获取.
* (2), JobDataMap可以用来装载任何可序列化的数据对象,当Job实例对象被执行时,这些参数对象会传递给它.
* (3), JobDateMap实现了JDK的Map接口,并且添加了一些非常方便的方法用来存取基本数据类型.
* 获取方试:
* */
//获取传入的Job参数信息;
JobDataMap jobDataMap = context.getJobDetail().getJobDataMap();
System.out.println("Job param: "+jobDataMap.getString("message")+", "+jobDataMap.getFloat("FloatMessage"));
//获取传入的Trigger参数信息;
JobDataMap triggerDataMap = context.getTrigger().getJobDataMap();
System.out.println("Trigger param: "+triggerDataMap.getString("message")+", "+triggerDataMap.getDouble("DoubleMessage"));
/* --------------获取JobDataMap方法二: 合并获取--------------- */
JobDataMap mergedJobDataMap = context.getMergedJobDataMap();
System.out.println("ALL param: "+mergedJobDataMap.getString("message")+", "+mergedJobDataMap.getFloat("FloatMessage")+", "+mergedJobDataMap.getDouble("DoubleMessage")+"\n");
/**
* 测试证明: 当Trigger与JobDetail中传入的Key值相同,使用mergedJobDataMap获取,则优先获取Trigger中的Key值;JobDetail中的Key值被覆盖掉;
* */
}
}
(3), 通过set,get方法获取参数:
package quartz;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.JobKey;
import org.quartz.TriggerKey;
/**
* (1),Job接口非常容易实现,只有一个execute方法,类似TimerTask的run方法,在里面编写业务逻辑;
* (2),Job实例在Quartz中的生命周期:
* 每次调度器执行Job时,它在调用execute方法前会创建一个新的Job实例.当调用完成后,关联的Job对象实例会被释放,之后会被垃圾回收;
**/
public class HelloJob implements Job{
private String message;
private float FloatMessage;
private double DoubleMessage;
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public float getFloatMessage() {
return FloatMessage;
}
public void setFloatMessage(float floatMessage) {
FloatMessage = floatMessage;
}
public double getDoubleMessage() {
return DoubleMessage;
}
public void setDoubleMessage(double doubleMessage) {
DoubleMessage = doubleMessage;
}
/**
* JobExecutionContext是什么:
* (1), 当Scheduler(调度器) 调用一个Job,就会将JobExecutionContext传递给Job的execute()方法;
* (2), Job能通过JobExecutionContext对象访问到Quartz运行时候的环境及Job本身的明细数据;
**/
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
Calendar calender = Calendar.getInstance();
SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// TODO 具体的业务逻辑:
System.out.println("Hellow Quartz! 执行时间:"+sf.format(calender.getTime()));
//获取传入的任务属性信息;
JobKey jobKey = context.getJobDetail().getKey();
System.out.println("Job(任务) : name: "+jobKey.getName()+", group: "+jobKey.getGroup());
//获取传入的触发器属性信息;
TriggerKey triggerKey = context.getTrigger().getKey();
System.out.println("Trigger(触发器) : name: "+triggerKey.getName()+", group: "+triggerKey.getGroup());
/**
* JobDataMap:
* (1), 在进行任务调度时JobDataMap存储,在JobExecutionContext中,非常方便获取.
* (2), JobDataMap可以用来装载任何可序列化的数据对象,当Job实例对象被执行时,这些参数对象会传递给它.
* (3), JobDateMap实现了JDK的Map接口,并且添加了一些非常方便的方法用来存取基本数据类型.
* 获取方试:
* */
//获取传入的Job参数信息;
JobDataMap jobDataMap = context.getJobDetail().getJobDataMap();
System.out.println("Job param: "+jobDataMap.getString("message")+", "+jobDataMap.getFloat("FloatMessage"));
//获取传入的Trigger参数信息;
JobDataMap triggerDataMap = context.getTrigger().getJobDataMap();
System.out.println("Trigger param: "+triggerDataMap.getString("message")+", "+triggerDataMap.getDouble("DoubleMessage"));
/* --------------获取JobDataMap方法二: 合并获取--------------- */
JobDataMap mergedJobDataMap = context.getMergedJobDataMap();
System.out.println("ALL param: "+mergedJobDataMap.getString("message")+", "+mergedJobDataMap.getFloat("FloatMessage")+", "+mergedJobDataMap.getDouble("DoubleMessage"));
/**
* 测试证明: 当Trigger与JobDetail中传入的Key值相同,使用mergedJobDataMap获取,则优先获取Trigger中的Key值;JobDetail中的Key值被覆盖掉;
* */
/* --------------获取JobDataMap方法三: 属性 get,set--------------- */
System.out.println("ALL get param: "+message+", "+FloatMessage+", "+DoubleMessage+"\n");
/**
* Job实现类中创建setter方法对应JobDataMap的键值(Quartz框架默认的JobFactory实现类在初始化Job实例对象时会自动调用这些setter方法);
* */
}
}
6, Trigger 设置开始/结束 触发时间:
package quartz;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.SimpleScheduleBuilder;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.impl.StdSchedulerFactory;
public class HelloScheduler {
public static void main(String[] args) throws SchedulerException {
Calendar calender = Calendar.getInstance();
SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
/**
* 创建一个 JobDetail 实例,将该实例与 HelloJob绑定;
*
* JobDetail为Job实例提供了许多设置属性,以及JobDataMap成员变量属性,它用来储存特定Job实例的状态信息,调度器需要借助JobDetail对象来添加Job实例.
* 重要属性: name, group, jobClass, jobDataMap
* */
JobDetail jobDetail = JobBuilder.newJob(HelloJob.class).withIdentity("myJob","group1")
.usingJobData("message", "HelloJobDetail")
.usingJobData("FloatMessage", 2.2F)
.build();
// 创建一个Trigger实例,定义该job立即执行,并且每隔1秒钟重复执行一次;
/**
* Trigger是Quartz中的出触发器,用来告诉调度任务作业什么时候触发.即Trigger对象是用来触发Job的;
* 通用属性:
* JobKey: 表示Job实例的标识,触发器被触发时,该指定的Job实例会被执行;
* StartTime: 表示触发器的时间表 首次被触发的时间.值为java.util.Date;
* EndTime: 指定触发器的不在被触发的时间.值的类型是java.util.Date;
* */
//开始执行时间为当前时间的3秒后
Date startTime = new Date();
startTime.setTime(startTime.getTime()+3000);
//结束执行时间为当前时间的6秒后
Date endTime = new Date();
endTime.setTime(endTime.getTime()+6000);
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("myTrigger")
.usingJobData("DoubleMessage", 2.4D)
//设置开始/结束时间
.startAt(startTime)
.endAt(endTime)
.withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(1).repeatForever())
.build();
// 创建 Scheduler 实例;
SchedulerFactory sfact = new StdSchedulerFactory();
Scheduler scheduler = sfact.getScheduler();
scheduler.scheduleJob(jobDetail, trigger);
System.out.println("当前时间:"+sf.format(calender.getTime()));
scheduler.start();
}
}
Job中也可以获取 开始/结束 时间:
public class HelloJob implements Job{
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
Calendar calender = Calendar.getInstance();
SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// TODO 具体的业务逻辑:
System.out.println("Hellow Quartz! 执行时间:"+sf.format(calender.getTime()));
/* --------------------------获取开始/结束时间--------------------- */
Trigger trigger = context.getTrigger();
Date startTime = trigger.getStartTime();
Date endTime = trigger.getEndTime();
System.out.println("StartTime is "+sf.format(startTime));
System.out.println("EndTime is "+sf.format(endTime)+"\n");
}
}
7, SimpleTrigger设置执行条件:
/**
* SimpleTrigger: 在一个指定时间段内执行一次任务,或在指定时间间隔内多次执行任务;
* 注意: (1)重复次数可以为0,正整数或是 SimpleTrigger.REPEAT_INDEFINITELY 常量值.
* (2)重复执行时间间隔必须为0或长整数;
* (3)一担被指定了endTime参数,那么它会覆盖重复次数参数的效果;
* */
SimpleTrigger trigger = (SimpleTrigger)TriggerBuilder.newTrigger()
.withIdentity("myTrigger")
.usingJobData("DoubleMessage", 2.4D)
//设置开始/结束时间
.startAt(startTime)
.endAt(endTime)
//设置多次执行条件
.withSchedule(
SimpleScheduleBuilder
.simpleSchedule()
//时间间隔,单位有: 秒, 毫秒, 分钟, 小时
.withIntervalInSeconds(1)
//循环执行
// .repeatForever()
//执行次数, (参数+1)次, 参数为0执行1次,参数为1执行2次,...; 参数为-1,则执行无穷
.withRepeatCount(-1)
)
.build();
到这我们已经对Quartz有了一定的认识,那么下次再来分享更复杂的任务调度方式;看看怎么在某个节日来调度的.