quartz
一、基本概念
-
任务调度:
帮助我们设置一个有规律的或者在某个具体时间可以干一些特定的事
-
在java的编程世界中该怎么实现任务调度呢:
常见的解决方案:
- Timer:能实现简单的功能,功能一般,但是是JDK中自带的
- ScheduledExecutor:功能比较强大,但是是串行,不能解决并发的或者服务的任务调度
- Quartz:功能强大,并行工作,能基本上满足正常的业务逻辑中的任务调度
-
Quartz:
是OpenSymphony开源组织在Job scheduling领域又一个开源项目,它可以与J2EE与J2SE应用程序相结合也可以单独使用。Quartz可以用来创建简单或为运行十个,百个,甚至是好几万个Jobs这样复杂的程序。
Jobs可以做成标准的Java组件或 EJBs
简而言之:QZ就是能帮助我们实现任务调度的一个框架 -
Quartz的主要组成:
scheduler任务调度、Job任务、Trigger触发器、JobDetail任务细节
-
scheduler任务调度:
是最核心的概念,需要把JobDetail和Trigger注册到scheduler中,才可以执行
-
Job任务:
其实Job是接口,其中只有一个execute方法
-
Trigger触发器
- a)作用:它是来执行工作任务,在什么条件下触发,什么时间执行,多久执行一次。
- b)四大类型:SimpleTrigger,CronTirgger,DateIntervalTrigger, 和dailytimeintervaltrigger
-
JobDetail任务细节 : 用于定义作业/任务的实例。
额外补充:
-
JobBuilder - 用于定义/构建JobDetail实例,用于定义作业的实例
-
TriggerBuilder - 用于定义/构建触发器实例。
-
Quartz的特点:
作为一个优秀的开源调度框架,Quartz 具有以下特点:
- 1) 强大的调度功能,例如支持丰富多样的调度方法,可以满足各种常规及特殊需求;
- 2)灵活的应用方式,例如支持任务和调度的多种组合方式,支持调度数据的多种存储方式;帮助理解:Quartz 调度包的两个基本单元是作业和触发器。作业 是能够调度的可执行任务,触发器 提供了对作业的调度。虽然这两个实体很容易合在一起,但在 Quartz 中将它们分离开来是有原因的,而且也很有益处。通过把要执行的工作与它的调度分开,Quartz 允许在不丢失作业本身或作业的上下文的情况下,修改调度触发器。而且,任何单个的作业都可以有多个触发器与其关联
- 3)分布式和集群能力,Terracotta 收购后在原来功能基础上作了进一步提升。
- 4)另外,作为 Spring 默认的调度框架,Quartz 很容易与 Spring 集成实现灵活可配置的调度功能
-
Quartz中用到的设计模式(了解):
- Builder模式:建造者模式:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示,目的是为了实现解耦
- Factory模式:工厂模式
- 组件模式:QZ的组件可以很好的实现插拔
- 链式模式:QZ的代码书写是支持链式分解的
二、QZ的基本用法
第一个基本的例子:实现控制台的间隔打印
创建一个非web项目Maven_QuartzLx1
1.引入QZ的依赖:
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.3.0</version>
</dependency>
2.创建JOb任务类:
HelloWorldJob.java(切记一定要实现QZ的job接口),实现execute()方法
import java.text.SimpleDateFormat;
import java.util.Date;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
// @desc JOb任务类
public class HelloWorldJob implements Job{
public void execute(JobExecutionContext context) throws JobExecutionException {
String strTime = new SimpleDateFormat("HH-mm-ss").format(new Date());
System.out.println( strTime + ":Hello World!");
}
}
3.创建一个任务调度器MyScheduler.java:
- 1)创建调度器Schedule(通过工厂模式创建)
- 2)创建JobDetail实例,并与HelloWordlJob类绑定(通过建造者模式创建)
- 3)创建触发器Trigger实例并设定触发规则(立即执行,每隔1S执行一次)
- 4)开始执行调度任务:将任务和触发器都交给调度器进行管理
- 5)调度结束(不是必须的)
import org.quartz.CronScheduleBuilder;
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;
//利用QZ中的复杂的触发器:cronTrigger
@SuppressWarnings("unused")
public class MyScheduler {
public static void main(String[] args) throws SchedulerException, InterruptedException {
//1.创建调度器Schedule(通过工厂模式创建)
SchedulerFactory schedulerFactory = new StdSchedulerFactory();
Scheduler scheduler = schedulerFactory.getScheduler();
//2.创建JobDetail实例,并与HelloWordlJob类绑定(通过建造者模式创建)
JobDetail jobDetail = JobBuilder.newJob(HelloWorldJob.class).withIdentity("job1", "jobGroup1").build();
//3.创建触发器Trigger实例(立即执行,每隔1S执行一次)
Trigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger1", "triggerGroup1")
.startNow()
.withSchedule(CronScheduleBuilder.cronSchedule("0 38 17 * * ? "))
.build();
/**1.CronTrigger
适合于更复杂的任务,它支持类型于Linux Cron的语法(并且更强大)。基本上它覆盖了以上三个Trigger的绝大部分能力(但不是全部)—— 当然,也更难理解。
它适合的任务类似于:每天0:00,9:00,18:00各执行一次。
它的属性只有:Cron表达式。但这个表示式本身就够复杂了
*/
//4.开始执行调度任务
scheduler.scheduleJob(jobDetail, trigger);
scheduler.start();
//5.调度结束
Thread.sleep(10000);
System.out.println("任务调度结束了....");
scheduler.shutdown();//结束调度
}
}
第二个例子:每日的9点40分触发发送邮件
CronTrigger:
适合于更复杂的任务,它支持类型于Linux Cron的语法(并且更强大)。基本上它覆盖了以上三个Trigger的绝大部分能力(但不是全部)—— 当然,也更难理解。它适合的任务类似于:每天0:00,9:00,18:00各执行一次。
它的属性只有:Cron表达式。但这个表示式本身就够复杂了(图片为网上资源,如有版权,请联系)
1.MyScheduler.java:利用QZ中的复杂的触发器:cronTrigger
import org.quartz.CronScheduleBuilder;
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;
@SuppressWarnings("unused")
public class MyScheduler {
public static void main(String[] args) throws SchedulerException, InterruptedException {
//1.创建调度器Schedule(通过工厂模式创建)
SchedulerFactory schedulerFactory = new StdSchedulerFactory();
Scheduler scheduler = schedulerFactory.getScheduler();
//2.创建JobDetail实例,并与HelloWordlJob类绑定(通过建造者模式创建)
JobDetail jobDetail = JobBuilder.newJob(HelloWorldJob.class).withIdentity("job1", "jobGroup1").build();
//3.创建触发器Trigger实例(立即执行,每隔1S执行一次)
Trigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger1", "triggerGroup1")
.startNow()
.withSchedule(CronScheduleBuilder.cronSchedule("0 38 17 * * ? "))
.build();
/**
* 1.CronTrigger
适合于更复杂的任务,它支持类型于Linux Cron的语法(并且更强大)。基本上它覆盖了以上三个Trigger的绝大部分能力(但不是全部)—— 当然,也更难理解。
它适合的任务类似于:每天0:00,9:00,18:00各执行一次。
它的属性只有:Cron表达式。但这个表示式本身就够复杂了
*/
//4.开始执行调度任务
scheduler.scheduleJob(jobDetail, trigger);
scheduler.start();
//5.调度结束
Thread.sleep(10000);
System.out.println("任务调度结束了....");
scheduler.shutdown();//结束调度
}
}
2.HelloWorldJob.java:JOb任务类
import java.text.SimpleDateFormat;
import java.util.Date;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
public class HelloWorldJob implements Job{
public void execute(JobExecutionContext context) throws JobExecutionException {
//打印当前的执行时间 例如 2017-11-22 00:00:00
Date date = new Date();
SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println("现在的时间是:"+ sf.format(date));
//具体的业务逻辑
System.out.println("开始生成任务报表 或 开始发送邮件");
}
}
第三个例子:JobDetail & Job
1.JobDetail&Job的概念的理解和区别
- 1)JobDetail:JobDetail是任务的定义,可以在里面定义各种任务执行的规则,以及任务的name,group,Description等
- 2)Job:Job是任务的执行逻辑
简而言之:jobdetail 就是对job的定义,而job是具体执行的逻辑内容
2.思考导读:
为什么需要有个JobDetai来作为job的定义,为什么不直接使用job
使用jobdetail来定义,那么每次调度都会创建一个new job实例,这样带来的好处就是任务并发执行的时候,互不干扰,不会对临界资源造成影响。这个也是软件解耦的一个体现,
3.补充导读:
Quartz调度一次任务,会干如下的事:
JobClass jobClass=JobDetail.getJobClass()
Job jobInstance=jobClass.newInstance()。所以Job实现类,必须有一个public的无参构建方法。
jobInstance.execute(JobExecutionContext context)。JobExecutionContext是Job运行的上下文,可以获得Trigger、Scheduler、JobDetail的信息
从这个补充导读的代码中我们就可以看明白其实每次调度都会创建一个新的job实例
4.job每次都是newInstance的实例,那我怎么传值给它?
比如我现在有两个发送邮件的任务,一个是发给"liLei",一个发给"hanmeimei",不能说我要写两个Job实现类LiLeiSendEmailJob和HanMeiMeiSendEmailJob。实现的办法是通过JobDataMap
5.JobDataMap:
每一个JobDetail都会有一个JobDataMap。JobDataMap本质就是一个Map的扩展类,只是提供了一些更便捷的方法,比如getString()之类的
6.例题
1.MyScheduler.java:JobDetail&Job&JobDataMap
package com.rj.bd.sche3;
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 MyScheduler {
public static void main(String[] args) throws SchedulerException, InterruptedException {
//1.创建调度器Schedule(通过工厂模式创建)
SchedulerFactory schedulerFactory = new StdSchedulerFactory();
Scheduler scheduler = schedulerFactory.getScheduler();
//2.创建JobDetail实例,并与Job逻辑处理类HelloWorldJob实现了绑定(通过建造者模式)
JobDetail jobDetail=JobBuilder.newJob(HelloWorldJob.class).withIdentity("job1", "jobGroup1")
.withDescription("这是job的描述:当前的job逻辑处理类为:HelloWorldJob")
.usingJobData("message", "可以传递map类型的参数值:666")//将当前的属性变量age加入到JobDataMap中
.build();
//3.创建Trigger实例()
Trigger trigger=TriggerBuilder.newTrigger().withIdentity("trigger1", "triggerGroup1")
.startNow()
.withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(1).repeatForever())
.build();
//4.开始执行调度任务:将任务和触发器都交给调度器进行管理
scheduler.scheduleJob(jobDetail, trigger);
scheduler.start();
//5.调度结束
}
}
2.HelloWorldJob.java:JOb任务类
import java.text.SimpleDateFormat;
import java.util.Date;
import org.quartz.DisallowConcurrentExecution;
import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobDetail;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.JobKey;
import org.quartz.PersistJobDataAfterExecution;
@PersistJobDataAfterExecution
@DisallowConcurrentExecution
public class HelloWorldJob implements Job{
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
System.out.println("。。。。。。。。。。。。");
JobDetail detail = jobExecutionContext.getJobDetail();
JobKey keys = detail.getKey();
System.out.print("JobDetail的name:"+keys.getName()+"\t");
System.out.print("JobDetail的group:"+keys.getGroup()+"\t");
Class<? extends Job> jobc = detail.getJobClass();
System.out.print("本次执行的任务工作类:"+jobc+"\t");
//下面是讲解JobDataMap的例子
JobDataMap map = detail.getJobDataMap();
System.out.print("JobDataMap:"+map.getString("message")+"\n");
}
}
(“JobDetail的group:”+keys.getGroup()+"\t");
Class<? extends Job> jobc = detail.getJobClass();
System.out.print(“本次执行的任务工作类:”+jobc+"\t");
//下面是讲解JobDataMap的例子
JobDataMap map = detail.getJobDataMap();
System.out.print(“JobDataMap:”+map.getString(“message”)+"\n");
}
}