首先上下依赖
最好把log4j的依赖也添加进去,quartz需要打印日志,这里就不添加了.
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.3.0</version>
</dependency>
贴一个最简单的实例
import com.anjiplus.job.Myjob1;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import java.util.Properties;
public class Test {
public static void main(String[] args) throws Exception {
Scheduler scheduler = getScheduler();
JobDetail jobDetail = JobBuilder.newJob(Myjob1.class).build();
scheduler.start();
Trigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger1", "group1")
.startNow()
.withSchedule(
SimpleScheduleBuilder.simpleSchedule()
.withIntervalInSeconds(1)
.repeatForever()
)
.build();
scheduler.scheduleJob(jobDetail, trigger);
}
public static Scheduler getScheduler() throws Exception {
Properties prop = new Properties();
prop.load(Test.class.getResourceAsStream("/quartz.properties"));
StdSchedulerFactory stdSchedulerFactory = new StdSchedulerFactory(prop);
Scheduler scheduler = stdSchedulerFactory.getScheduler();
return scheduler;
}
}
//
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import java.util.Date;
public class Myjob1 implements Job {
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
System.out.println(new Date()+" myJob1");
}
}
打印结果:
Mon Aug 03 15:58:43 CST 2020 myJob1
Mon Aug 03 15:58:44 CST 2020 myJob1
Mon Aug 03 15:58:45 CST 2020 myJob1
Mon Aug 03 15:58:46 CST 2020 myJob1
Mon Aug 03 15:58:47 CST 2020 myJob1
Mon Aug 03 15:58:48 CST 2020 myJob1
以上是最简单的一个quartz调度.
Scheduler是全局调度,通过将JobDetail与Trigger添加到其中,用来监控任务的执行,所以他是最主要的.
Scheduer的获取是通过SchedulerFactory来获取的,SchedulerFactory是个接口,其有两个实现类:StdSchedulerFactory和DirectSchedulerFactory,他们两个的区别是DirectSchedulerFactory是硬编码,需要把配置写在代码之中,一般不会采用这个类来获取Scheduler,所以直接记住StdSchedulerFactory就好,上述例子中,传入一个quartz.properties就是读取配置文件.(配置文件稍后再说)
Scheduer需要调用.start()方法才会真正地调度job执行,通过shutdown()方法去销毁,并且可以随时增加和删除Job,但只有start()后任务才会真正被调度.
Job是一个接口,继承Job接口后实现excute方法,在exceute方法中书写Job执 行逻辑.
JobDetail 负责job的实例化,它代表着Job的多次执行,而job代表着每次启动都是需要一个job的实例,比如说一个job要运行N次,JobDetail代表着N次,而Job代表着一次.
JobDetail中有job的属性,其中很重要的是JobDataMap,它是JobDetail与Job通信的关键,可以设置JobDataMap是否每一次都更新,比如说在调度之前,JobDetail中添加JobDataMap中"count"’->0 来统计job执行次数,这样每次执行的job都可以获取JobDataMap中的count,是允许运行的job更改count值,并保存,还是不允许呢,既然是统计job执行次数,当然是允许更改了,每次都把count变量加1,不然的话每次获取的count都是0.至于如何设置,后面再说.
上面定义任务运行的是JobDetail和Job,但是何时运行就是Trigger
Trigger是个接口有四个子接口
CronTrigger
SimpleTrigger
DailyTimeIntervalTrigger
CalendarIntervalTrigger
以上的类我们不需要关心,因为quartz通过建造者模式,传入SchdulerBuiler来决定到底是哪种类型的Trigger.稍后说如何创建Trigger
上面对重要组件有了一定的认识,下面通过重要Api继续说明:
/*读取配置文件并根据配置文件创建Scheduler*/
Properties prop = new Properties();
prop.load(Test.class.getResourceAsStream("/quartz.properties"));
StdSchedulerFactory stdSchedulerFactory = new StdSchedulerFactory(prop);
Scheduler scheduler = stdSchedulerFactory.getScheduler();
/*绑定JobDetail按照Trigger定义的计划去执行*/
scheduler.scheduleJob(jobDetail, trigger);
scheduler.start();
//scheduler.shutdown();
上面代码就是创建Scheduler.quartz.properties放在了resources目录下.配置文件中的配置要最后说,里面内容比较多.
JobDetail的创建:
JobDetail jobDetail = JobBuilder
/*传递我们的Job*/
.newJob(Myjob1.class)
.withIdentity("myjob")
/*意外中止,将会在重启时,先补充前面未执行的批次*/
//.requestRecovery(true)
/*设置obDataMap中的值,也可以通过JobDetail.getJobDataMap.put(k,v)来设置*/
.usingJobData("count",0)
.build();
Job创建:
@DisallowConcurrentExecution
@PersistJobDataAfterExecution
//继承Job类
public class Myjob implements Job {
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
try {
//可以获取到JobDetail
JobDetail jobDetail = context.getJobDetail();
//JobDataMap
JobDataMap jobDataMap = jobDetail.getJobDataMap();
int count = (Integer) jobDataMap.get("count") + 1;
jobDataMap.put("count",count);
System.out.println("myjob的第"+count+"次执行");
//你要执行的逻辑写在这里
} catch (Exception e) {
/*如果想要让job抛出异常被Scheduler捕获,就必须是JobExecutionException异常,
并且下面告诉了Schduler应该接下怎么怎么做*/
JobExecutionException jobE = new JobExecutionException(e.getMessage());
/*立即重新执行*/
//jobE.refireImmediately();
/*立即取消所有与这个Job关联的trigger*/
//jobE.setUnscheduleAllTriggers(true);
/*如果不做任何处理,下次调度正常执行,就不需要设置任何内容,直接抛出*/
throw jobE;
} finally {
}
}
}
@DisallowConcurrentExecution
job是否允许并发,比如定义一个每秒中执行一次的job,如果上一个job没有执行完,是否允许下一个job执行,这个注解是不允许.
@PersistJobDataAfterExecution
job执行中,可以更新JobDataMap,每次拿到的JobDataMap都是上个job更新后的结果.因为上述JobDetail中初始化了一个"count"变量,所以上述代码执行结果就是
myjob的第1次执行
myjob的第2次执行
myjob的第3次执行
…
如果没有这个注解
myjob的第1次执行
myjob的第1次执行
myjob的第1次执行
…
excute方法中只有抛出JobExecutionException才能被Shecduler捕获,抛出其他异常,Scheduler将不会做任何处理.
上面说了Tigger有四个实现类,但是这里只说两个,因为这两个基本就可以满足我们的定时了.
1) SimpleTrigger
SimpleTrigger trigger = TriggerBuilder
.newTrigger()
.startNow()
/*指定trigger的开始-结束时间*/
//.startAt(new Date())
//.endAt(new Date())
/*trigger的优先级任意整数,默认5
同时出发的trigger优先级越高越先执行*/
//.withPriority(5)
/*跳过某些时间下面会说*/
//.modifiedByCalendar("myCalendar")
.withIdentity("trigger1", "group1")
.withSchedule(
//通过这里的Scheduler来确定的build()之后具体得到的是什么Trigger
SimpleScheduleBuilder.simpleSchedule()
/*job间隔时间*/
.withIntervalInSeconds(1)
/*job执行几次*/
//.withRepeatCount(1)
.repeatForever()
)
.build();
2) CronTrigger
.withSchedule(
//通过这里的Scheduler来确定的build()之后具体得到的是什么Trigger
CronScheduleBuilder.cronSchedule("*/1 * * * * *")
/*设置时区,默认是运行server的时区*/
//.inTimeZone()
)
.build();
CronTrigger就是基于con表达式去执行,基本可以满足无限循环的job
SimpleTrigger就是简单的执行几次,每一次的间隔
在上方SimpleTrigger代码中有一个.modifiedByCalendar(“myCalendar”)方法,这里设置的是一个排除的日期,这里的"myCalendar"是添加到Scheduler中的,trigger引用就通过name引用
HolidayCalendar myCalendar= new HolidayCalendar();
//添加配出时间
myCalendar.addExcludedDate(Date1);
myCalendar.addExcludedDate( Date2 );
scheduler.addCalendar("myCalendar", myCalendar, false,true);
Calendar是一个接口里面很多实现类,可以自己去查看.
另外还有一个是Misfire机制,它是作用在持久化的任务上的,如果因为trigger线程问题(任务过多,有些tigger没有在合适时间执行),或者意外宕机等导致的任务没有执行,需要补上任务的,可以查看misfire机制.
quartz.properties
# 固定前缀org.quartz
# 主要分为scheduler、threadPool、jobStore、plugin等部分
# 实例化ThreadPool时,使用的线程类为SimpleThreadPool
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
org.quartz.scheduler.instanceName = myquartz
# threadCount和threadPriority将以setter的形式注入ThreadPool实例
# 并发个数
org.quartz.threadPool.threadCount = 300
# 优先级
org.quartz.threadPool.threadPriority = 5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true
org.quartz.jobStore.misfireThreshold = 5000
# 默认存储在内存中
org.quartz.jobStore.class = org.quartz.simpl.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.dataSource = qzDS
#org.quartz.dataSource.qzDS.driver = com.mysql.cj.jdbc.Driver
#org.quartz.dataSource.qzDS.URL = jdbc:mysql://ip:port/库名?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
#org.quartz.dataSource.qzDS.user = dev
#org.quartz.dataSource.qzDS.password = dev
#org.quartz.dataSource.qzDS.maxConnections = 10
#org.quartz.dataSource.qzDS.connectionProvider.class=xxxx
org.quartz.jobStore.class 是非常重要的一个参数,有三个可选值,RAMJobStore,JobStoreTX,TerracottaJobStore
TerracottaJobStore是用于集群
RAMJobStore是使用内存保存job信息(使用最多)
JobStoreTX是使用数据库去保存信息,但是需要提前建表,需要的话建表语句官网查.(使用其次多)
了解以上内容后基本的开发中的job就可以满足需求了,还需要一些没有介绍的功能请查看官网
quartz官网2.3.0