1.Quartz简介(可参考官方文档https://www.w3cschool.cn/quartz_doc/quartz_doc-1xbu2clr.html)
//DSL静态导入
import static org.quartz.JobBuilder.newJob;
import static org.quartz.TriggerBuilder.newTrigger;
//下面的事调度器的接口的DSL静态导入
import static org.quartz.SimpleScheduleBuilder.*;
import static org.quartz.CronScheduleBuilder.*;
import static org.quartz.CalendarIntervalScheduleBuilder.*;
SchedulerFactory schedFact = new org.quartz.impl.StdSchedulerFactory();
Scheduler sched = schedFact.getScheduler();
sched.start();
//实例化一个job
JobDetail job = newJob(HelloJob.class)
.withIdentity("myJob", "group1")
.build();
//实例化trigger(40秒执行一次,永远执行)
Trigger trigger = newTrigger()
.withIdentity("myTrigger", "group1")
.startNow()
.withSchedule(simpleSchedule()
.withIntervalInSeconds(40)
.repeatForever())
.build();
//启动调度任务
sched.scheduleJob(job, trigger);
Quartz是一个任务调度框架,可以定时的去做某一件事情.
1. Scheduler:调度器。所有的调度都是由它控制
(1) 生命周期:SchedulerFactory创建它时开始,Scheduler只有在调用start()方法后,才会真正地触发trigger(即执行job),到Scheduler调用shutdown()方法时结束,Scheduler被创建后,可以增加、删除和列举Job和Trigger,以及执行其它与调度相关的操作(如暂停Trigger)。
(2)实例化: SchedulerBuilder接口的各种实现类,可以定义不同类型的调度计划(schedule)
2. Trigger: 定义触发的条件
(1)了解Trigger:Trigger用于触发Job的执行。当你准备调度一个job时,你创建一个Trigger的实例,然后设置调度相关的属性。
(2)区分类型: SimpleTrigger和CronTrigger
SimpleTrigger主要用于一次性执行的Job(只在某个特定的时间点执行一次),或者Job在特定的时间点执行,重复执行N次,每次执行间隔T个时间单位。
CronTrigger在基于日历的调度上非常有用,如“每个星期五的正午”,或者“每月的第十天的上午10:15”等。
(3)Trigger的公共属性 (构建trigger的时候可以通过TriggerBuilder设置)
TriggerKey属性: 表示trigger的身份
jobKey属性:当trigger触发时被执行的job的身份
startTime属性:设置trigger第一次触发的时间;该属性的值是java.util.Date类型,表示某个指定的时间点;有些类型的trigger,会在设置的startTime时立即触发,有些类型的trigger,表示其触发是在startTime之后开始生效
endTime属性:表示trigger失效的时间点
priority属性: 优先级 注意: (只有同时触发的trigger之间才会比较优先级。10:59触发的trigger总是在11:00触发的trigger之行。如果trigger是可恢复的,在恢复后再调度时,优先级与原trigger是一样的。)
misfire Instructions属性: 错过触发
calendar属性: 日历示例(Quartz的Calendar对象(不是java.util.Calendar对象)可以在定义和存储trigger的时候与trigger进行关联。Calendar用于从trigger的调度计划中排除时间段。------参考官网)
3. JobDetail: 定义的是任务数据,真正的执行逻辑是在Job中,JobDetail & Job 方式,sheduler每次执行,都会根据JobDetail创建一个新的Job实例,这样就可以规避并发访问的问题。
JobDetail job = newJob(HelloJob.class) .withIdentity("myJob", "group1") //定义job的身份 key,分组 .build();
4. Job: 调度程序执行组件的接口
(1):Job接口的execute方法
public class HelloJob implements Job { public HelloJob() { } public void execute(JobExecutionContext context) throws JobExecutionException { System.err.println("Hello! HelloJob is executing."); } }
(2):如何给job实例增加属性或配置(JobDataMap,JobDetail对象的一部分).
JobDataMap: JobDataMap中可以包含不限量的(序列化的)数据对象,在job实例执行的时候,可以使用其中的数据;JobDataMap是Java Map接口的一个实现,额外增加了一些便于存取基本类型的数据的方法。
将job加入到scheduler之前,在构建JobDetail时,可以将数据放入JobDataMap.
JobDetail job = newJob(DumbJob.class) .withIdentity("myJob", "group1") // name "myJob", group "group1" .usingJobData("jobSays", "Hello World!") //JobDataMap存放数据 .usingJobData("myFloatValue", 3.141f) JobDataMap存放数据 .build();
在job的执行过程中,可以从JobDataMap中取出数据使用
public class DumbJob implements Job { public DumbJob() { } public void execute(JobExecutionContext context) throws JobExecutionException { //去JobDetail的key值 JobKey key = context.getJobDetail().getKey(); //从JobDataMap取值 JobDataMap dataMap = context.getJobDetail().getJobDataMap(); String jobSays = dataMap.getString("jobSays"); float myFloatValue = dataMap.getFloat("myFloatValue"); System.err.println("Instance " + key + " of DumbJob says: " + jobSays + ", and val is: " + myFloatValue); } }
注意:JobDataMap中存储的对象都会被序列化,因此很可能会导致类的版本不一致的问题
在Job执行时,JobExecutionContext中的JobDataMap为我们提供了很多的便利。它是JobDetail中的JobDataMap和Trigger中的JobDataMap的并集,但是如果存在相同的数据,则后者会覆盖前者的值。
JobFactory也可以实现数据的自动“注入”
public class DumbJob implements Job { String jobSays; float myFloatValue; ArrayList state; public DumbJob() { } public void execute(JobExecutionContext context) throws JobExecutionException { JobKey key = context.getJobDetail().getKey(); JobDataMap dataMap = context.getMergedJobDataMap(); // Note the difference from the previous example state.add(new Date()); System.err.println("Instance " + key + " of DumbJob says: " + jobSays + ", and val is: " + myFloatValue); } public void setJobSays(String jobSays) { this.jobSays = jobSays; } public void setMyFloatValue(float myFloatValue) { myFloatValue = myFloatValue; } public void setState(ArrayList state) { state = state; } }
(3)Job实例
可以只创建一个job类,然后创建多个与该job关联的JobDetail实例,每一个实例都有自己的属性集和JobDataMap,最后, 将所有的实例都加到scheduler中。比如,你创建了一个实现Job接口的类“SalesReportJob”。该job需要一个参数(通过JobdataMap传入),表示负责该销售报告的销售员的名字。因此,你可以创建该job的多个实例(JobDetail),比如“SalesReportForJoe”、“SalesReportForMike”,将“joe”和“mike”作为JobDataMap的数据传给对应的job实例。
当一个trigger被触发时,与之关联的JobDetail实例会被加载,JobDetail引用的job类通过配置在Scheduler上的JobFactory进行初始化。默认的JobFactory实现,仅仅是调用job类的newInstance()方法,然后尝试调用JobDataMap中的key的setter方法。
(4)Job状态与并发
@DisallowConcurrentExecution:将该注解加到job类上,告诉Quartz不要并发地执行同一个job定义(这里指特定的job类)的多个实例。该限制是针对JobDetail的,而不是job类的。
@PersistJobDataAfterExecution:将该注解加在job类上,告诉Quartz在成功执行了job类的execute方法后(没有发生任何异常),更新JobDetail中JobDataMap的数据,使得该job(即JobDetail)在下一次执行的时候,JobDataMap中是更新后的数据,而不是更新前的旧数据。该限制是针对JobDetail的,而不是job类的。因为job类的内容经常会影响其行为状态(比如,job类的execute方法需要显式地“理解”其”状态“)。
注意:如果使用了@PersistJobDataAfterExecution注解,我们强烈建议你同时使用@DisallowConcurrentExecution注解,因为当同一个job(JobDetail)的两个实例被并发执行时,由于竞争,JobDataMap中存储的数据很可能是不确定的。
(5)Job的其它特性
Durability:如果一个job是非持久的,当没有活跃的trigger与之关联的时候,会被自动地从scheduler中删除。也就是说,非持久的job的生命期是由trigger的存在与否决定的;
RequestsRecovery:如果一个job是可恢复的,并且在其执行的时候,scheduler发生硬关闭(hard shutdown)(比如运行的进程崩溃了,或者关机了),则当scheduler重新启动的时候,该job会被重新执行。此时,该job的JobExecutionContext.isRecovering() 返回true。
(6)JobExecutionException
这里暂时不做介绍了,可以关注官方文档
2.springBoot简单整和quartz
1.导入maven依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-quartz</artifactId> </dependency> <dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz</artifactId> </dependency>
2.直接上java代码(我并没有在配置中配置信息,若你想配置可参考官方文档)
package org.medical.controller.quartz; import org.quartz.JobDetail; import org.quartz.Scheduler; import org.quartz.SchedulerException; import org.quartz.Trigger; import org.quartz.impl.StdSchedulerFactory; import org.springframework.stereotype.Component; import static org.quartz.TriggerBuilder.newTrigger; import org.quartz.DateBuilder; import static org.quartz.JobBuilder.newJob; import static org.quartz.SimpleScheduleBuilder.simpleSchedule; import static org.quartz.CronScheduleBuilder.weeklyOnDayAndHourAndMinute;; @Component public class TestQuartzDemo { public static void asdf() throws SchedulerException, InterruptedException { //创建Scheduler对象 Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler(); /*定义一个SimpleTrigger Trigger trigger = newTrigger().withIdentity("trigger1", "group1") //定义name/group .startNow()//一旦加入scheduler,立即生效 .withSchedule(simpleSchedule() //使用SimpleTrigger .withIntervalInSeconds(1) //每隔一秒执行一次 .withRepeatCount(10)) //执行1000次 .build();*/ //定义一个cronTrigger Trigger trigger = newTrigger() .withIdentity("trigger3", "group1") //代表trigger的身份,key/分组 .withSchedule(weeklyOnDayAndHourAndMinute(DateBuilder.MONDAY, 17, 13)) //周一下午5.13执行 .build(); //定义一个JobDetail JobDetail job = newJob(HelloQuartz.class) //定义Job类为HelloQuartz类,这是真正的执行逻辑所在 .withIdentity("job1", "group1") //定义name/group .usingJobData("name", "quartz") //定义属性 .build(); //加入这个调度 scheduler.scheduleJob(job, trigger); //启动之 scheduler.start(); } }
定义job
package org.medical.controller.quartz; import java.util.Date; import org.quartz.Job; import org.quartz.JobDetail; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.quartz.JobKey; public class HelloQuartz implements Job{ public void execute(JobExecutionContext context) throws JobExecutionException { JobDetail detail = context.getJobDetail(); JobKey key = context.getJobDetail().getKey(); String name = detail.getJobDataMap().getString("name"); System.out.println("key"+key+"say hello to " + name + " at " + new Date()); } }
注释:以上只是根据官方文档,及个人测试用,验证无误,简单的应用,欢迎大家投稿,多多支持小弟
3.springBoot整合quartz(解决无法注入spring Bean),直接上代码吧
package org.medical.controller.quartz2;
import org.quartz.spi.TriggerFiredBundle;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.scheduling.quartz.AdaptableJobFactory;
import org.springframework.stereotype.Component;
@Component
public class MyJobFactory extends AdaptableJobFactory {
@Autowired
private AutowireCapableBeanFactory capableBeanFactory;
@Override
protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
// 调用父类的方法
Object jobInstance = super.createJobInstance(bundle);
// 进行注入
capableBeanFactory.autowireBean(jobInstance);
return jobInstance;
}
}
package org.medical.controller.quartz2;
import java.io.IOException;
import org.quartz.JobDetail;
import org.quartz.Trigger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.quartz.CronTriggerFactoryBean;
import org.springframework.scheduling.quartz.JobDetailFactoryBean;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
@Configuration
@EnableScheduling
public class QuartzSchedule {
@Autowired
private MyJobFactory myJobFactory;
@Bean
public SchedulerFactoryBean schedulerFactoryBean(Trigger updateTrigger) throws IOException {
SchedulerFactoryBean factory = new SchedulerFactoryBean();
factory.setOverwriteExistingJobs(true);
// 延时启动
//factory.setStartupDelay(20);
// 自定义Job Factory,用于Spring注入
factory.setJobFactory(myJobFactory);
factory.setTriggers(updateTrigger);
return factory;
}
@Bean(name = "updateJobDetail")
public JobDetailFactoryBean uploadJobDetail() {
JobDetailFactoryBean jobDetail = new JobDetailFactoryBean();
jobDetail.setJobClass(MyJob.class);
return jobDetail;
}
@Bean(name = "updateTrigger")
public CronTriggerFactoryBean updateTriggerFactoryBean(JobDetail updateJobDetail) {
CronTriggerFactoryBean trigger = new CronTriggerFactoryBean();
trigger.setCronExpression("0 54 11 ? * MON-FRI"); //周一到周五的11.54执行
trigger.setJobDetail(updateJobDetail);
return trigger;
}
}
package org.medical.controller.quartz2;
import java.io.Serializable;
import java.util.Date;
import java.util.Set;
import org.quartz.DisallowConcurrentExecution;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
/*
* 我这里注入的事redisTemplate 定时清除redis缓存
*/
@DisallowConcurrentExecution
public class MyJob implements Job, Serializable {
private static final long serialVersionUID = 1L;
private Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
private RedisTemplate<String, String> redisTemplate;
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
System.out.println(new Date());
Set<String> keys = redisTemplate.keys("*");
Long delete = redisTemplate.delete(keys);
System.out.println(delete);
}
}