一、了解Quartz
Quartz是OpenSymphony开源组织在Job scheduling领域又一个开源项目,Quartz 是一个完全由 Java 编写的开源作业调度框架,为在 Java 应用程序中进行作业调度提供了简单却强大的机制,可以与 J2EE与 J2SE应用程序相结合使用也可以单独使用。其允许程序开发人员根据时间的间隔来调度作业。
Quartz 实现了作业和触发器的多对多的关系,还能把多个作业与不同的触发器关联。
Quartz 的几个核心概念:
1.Job 表示一个工作,要执行的具体内容。此接口中只有一个方法void execute(JobExecutionContext context)
2.JobDetail 表示一个具体的可执行的调度程序,Job是这个可执行程调度程序所要执行的内容,另外 JobDetail 还包含了这个任务调度的方案和策略。
3.Trigger 代表一个调度参数的配置,什么时候去调。
4.Scheduler 代表一个调度容器,一个调度容器中可以注册多个 JobDetail 和 Trigger。当 Trigger 与 JobDetail 组合,就可以被 Scheduler 容器调度了。
二、Quartz的第一种使用方式
上面说到了Quartz既可以与 J2EE以及 J2SE应用程序相结合使用也可以单独使用,这里就先介绍一下单独使用的方法。
既然是单独使用,也就是说我们只需要一个main函数就可以开始一个调度任务。在进行Quartz调度时,最重要的三部分就是Job、Trigger、Scheduler,分别为任务、触发器和调度器。
把三者的关系打个比方,Scheduler就像是Boss主公,他是整个团队的核心领袖,负责整个调度运行的环境。而Trigger就像是谋士军师,他有整个工作要如何运行的标准。最后的Job就像是小兵,来执行老板下发的命令,以及Trigger告诉他要如何执行怎么执行,这样Job就去做准确的调度任务(可能不是特别准确,大家自己理解一下)。
第一步,需要添加Quartz依赖
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.3.0</version>
</dependency>
首先,创建我们的调度任务类,该类实现 Job接口,只有一个execute方法需要重写,我们把需要的相关业务代码写在此方法中,最后会将该方法加入调度器中执行。
/**
* @author Edwin
* @description 调度任务类
**/
public class QuartzTaskJob implements Job {
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
System.out.println("执行目标操作中...");
}
}
第二部分代码便是上面提到的,需要创建调度器 Scheduler、触发器 Trigger、及任务实例Job,这样我们就可以对该任务进行以我们赋予的标准或规则进行调度,下面是main函数中的代码
/**
* @author Edwin
* @description 调度控制类
**/
public class MyScheduler {
public static void main(String[] args) throws SchedulerException, InterruptedException {
//创建调度器Scheduler
SchedulerFactory schedulerFactory = new StdSchedulerFactory();
Scheduler scheduler = schedulerFactory.getScheduler();
//创建JobDetail
JobDetail jobDetail = JobBuilder.newJob(QuartzTaskJob.class)
.withIdentity("jobDetail", "group1").build();
//创建触发器Trigger
Trigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger1", "groupOfTrigger1")
.startNow()
.withSchedule(SimpleScheduleBuilder.simpleSchedule()
//每隔5秒执行一次
.withIntervalInSeconds(5)
.repeatForever()).build();
scheduler.scheduleJob(jobDetail, trigger);
System.out.println("调度开始执行...");
scheduler.start();
}
}
三、Spring boot 整合 Quartz
首先一步,还是需要在spring boot启动类上加上一个必不可少的注解 @EnableScheduling,这一步与spring boot自带的使用注解 @Scheduled做定时调度相同。
@SpringBootApplication
@EnableScheduling
public class CollectApplication {
public static void main(String[] args) {
SpringApplication.run(CollectApplication.class, args);
}
}
其次,在第一种Quartz的使用方法中我们用到了一个调度任务类,这里还是需要这个类,并实现Job接口和它的execute方法。
public class QuartzTaskJob implements Job {
@Resource
private JobService jobService;
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
System.out.println("jobService " + jobService.printSomeThing());
System.out.println("执行目标操作中...");
}
}
需要注意的一点,在上面介绍的第一种Quartz的使用方法中,在任务类里是无法注入spring boot的service的!!!
例如,在QuartzTaskJob类中写上
@Resource
private JobService jobService;
这是无效的,因为实例Job对象与上面注入的JobService是在不同的地方,Job对象的实例化是在Quartz中进行的,而JobService自然是由Spring容器本身来处理的。因此Quartz并不认识JobService,所以就无法使用它了。
于是,就有了第三步,需要创建一个Quartz工厂类并继承 AdaptableJobFactory类。在这个类里我们用到的一个不常见的类 AutowireCapableBeanFactory,AutowireCapableBeanFactory拥有自动装配功能,就像 Spring容器一样具有管理 Bean对象的能力。它在BeanFactory基础上实现了对实例的管理,可以使用这个接口集成其它框架,捆绑并填充并不由Spring管理生命周期并已存在的实例。这里就是将继承的父类AdaptableJobFactory创建出的 jobDetail对象放入该容器中。以下就是Quartz工厂类代码
//不要忘记 @Component注解
@Component
public class QuartzTaskFactory extends AdaptableJobFactory {
@Resource
private AutowireCapableBeanFactory autowireCapableBeanFactory;
@Override
protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
//创建JobDetail对象
Object jobDetail = super.createJobInstance(bundle);
//将jobDetail对象加入到Spring容器中
this.autowireCapableBeanFactory.autowireBean(jobDetail);
return jobDetail;
}
}
接下来最后一个重要的部分,在整合过程中我们需要创建一个Quartz配置类,在配置类中给容器注入我们重要的三个工厂类,SchedulerFactoryBean、CronTriggerFactoryBean或SimpleTriggerFactoryBean、JobDetailFactoryBean,其实就是分别对应着上面讲到的三个重要部分任务调度器 Scheduler、触发器 Trigger以及任务 Job,并对这三个类进行自己想要的操作。
先来依次认识一下这几个FactoryBean,最后我会把整个Quartz配置类的代码贴出来。
JobDetailFactoryBean:
spring对这个类的解释为:A Spring FactoryBean for creating a Quartz JobDetail instance, supporting bean-style usage for JobDetail configuration.一个用于创建Quartz JobDetail实例的,支持以bean定义风格来配置JobDetail的工厂bean。
@Bean
public JobDetailFactoryBean injectJobDetailFactoryBean(){
log.info("注入JobDetailFactoryBean...");
JobDetailFactoryBean factoryBean = new JobDetailFactoryBean();
//加入所要执行的任务Job
factoryBean.setJobClass(QuartzTaskJob.class);
return factoryBean;
}
CronTriggerFactoryBean和SimpleTriggerFactoryBean:
创建自己的TriggerFactoryBean触发器对象,构造调度触发标准,有两种形式的TriggerFactoryBean:
1.simpleTrigger 必须要配置两个属性,startDelay表示系统启动后多长时间开始执行第一次任务。repeatInterval表示执行任务的时间间隔。
2.cronTrigger 是用cron表达式来执行定时任务,与spring boot中 @Scheduled的配置cron相同,可以看上一篇文章中cron的使用。
@Bean(name="cronTriggerFactoryBean1")
public CronTriggerFactoryBean injectCronTriggerFactoryBean(){
log.info("注入CronTriggerFactoryBean...");
CronTriggerFactoryBean cronTriggerFactoryBean = new CronTriggerFactoryBean();
JobDetailFactoryBean jobDetailFactoryBean = this.injectJobDetailFactoryBean();
cronTriggerFactoryBean.setJobDetail(jobDetailFactoryBean.getObject());
//加入cron表达式,每五秒执行一次Job
cronTriggerFactoryBean.setCronExpression("0/5 * * * * ?");
return cronTriggerFactoryBean;
}
@Bean(name="simpleTriggerFactoryBean1")
public SimpleTriggerFactoryBean injectSimpleTriggerFactoryBean(){
SimpleTriggerFactoryBean simpleTriggerFactoryBean = new SimpleTriggerFactoryBean();
JobDetailFactoryBean jobDetailFactoryBean = this.injectJobDetailFactoryBean();
simpleTriggerFactoryBean.setJobDetail(jobDetailFactoryBean.getObject());
//程序启动后多久开始执行任务,单位ms 1000 = 一秒
simpleTriggerFactoryBean.setStartDelay(10000);
//每次定时任务的间隔时间(每隔多久执行一次),单位与上同
simpleTriggerFactoryBean.setRepeatInterval(5000);
return simpleTriggerFactoryBean;
}
SchedulerFactoryBean:
SchedulerFactoryBean主导着定时任务的初始化与执行顺序,SchedulerFactoryBean的常用属性:
triggers:triggers属性为Trigger[]类型,可以通过该属性注册多个Trigger
JobFactory:为Scheduler设置JobDetail的工厂。可以覆盖掉SpringBoot提供的默认工厂,保证JobDetail中的自动装配有效
calendars:类型为Map,通过该属性向Scheduler注册Calendar
jobDetails:类型为JobDetail[],通过该属性向Scheduler注册JobDetail
autoStartup:SchedulerFactoryBean在初始化后是否马上启动Scheduler,默认为true。如果设置为false,需要手工启动Scheduler
startupDelay:在SchedulerFactoryBean初始化完成后,延迟多长时间启动Scheduler
@Bean
public SchedulerFactoryBean injectSchedulerFactoryBean(QuartzTaskFactory quartzTaskFactory, CronTriggerFactoryBean[] cronTriggerFactoryBean(或者使用SimpleTriggerFactoryBean)){
log.info("注入SchedulerFactoryBean...");
SchedulerFactoryBean factoryBean = new SchedulerFactoryBean();
CronTrigger[] triggers = new CronTrigger[cronTriggerFactoryBean.length];
for(int i = 0; i < cronTriggerFactoryBean.length; i++){
triggers[i] = cronTriggerFactoryBean[i].getObject();
}
//注册触发器,一个Scheduler可以注册若干触发器
factoryBean.setTriggers(triggers);
//为Scheduler设置JobDetail的工厂。可以覆盖掉SpringBoot提供的默认工厂,保证JobDetail中的自动装配有效。
factoryBean.setJobFactory(quartzTaskFactory);
return factoryBean;
}
这样就完成了一个Quartz定时调度,下面贴上配置类的完整代码以方便大家使用。这些只是使用Quartz做较简单的调度工作,后面遇到其他业务中的使用或操作后再做新的文章。
@Configuration
@Slf4j
public class QuartzTaskConfiguration {
/**
* spring对这个类的解释为:A Spring FactoryBean for creating a Quartz JobDetail instance,
* supporting bean-style usage for JobDetail configuration.
* 一个用于创建Quartz JobDetail实例的,支持以bean定义风格来配置JobDetail的工厂bean。
*/
@Bean
public JobDetailFactoryBean injectJobDetailFactoryBean(){
log.info("注入JobDetailFactoryBean...");
JobDetailFactoryBean factoryBean = new JobDetailFactoryBean();
//加入所要执行的任务Job
factoryBean.setJobClass(QuartzTaskJob.class);
return factoryBean;
}
/**
* 定义自己的Trigger对象,有两种形式:
* 1.simpleTrigger 必须要配置两个属性,startDelay表示系统启动后多长时间开始执行第一次任务。repeatInterval表示执行任务的时间间隔。
* 2.cronTrigger 是用cron表达式来执行定时任务,与spring boot中 @Scheduled的配置cron相同,可以看上一篇文章。
*/
@Bean(name="cronTriggerFactoryBean1")
public CronTriggerFactoryBean injectCronTriggerFactoryBean(){
log.info("注入CronTriggerFactoryBean...");
CronTriggerFactoryBean cronTriggerFactoryBean = new CronTriggerFactoryBean();
JobDetailFactoryBean jobDetailFactoryBean = this.injectJobDetailFactoryBean();
cronTriggerFactoryBean.setJobDetail(jobDetailFactoryBean.getObject());
//加入cron表达式,每五秒执行一次Job
cronTriggerFactoryBean.setCronExpression("0/5 * * * * ?");
return cronTriggerFactoryBean;
}
/**
* 这里使用的是 CronTriggerFactoryBean触发方式
*/
@Bean
public SchedulerFactoryBean injectSchedulerFactoryBean(QuartzTaskFactory quartzTaskFactory, CronTriggerFactoryBean[] cronTriggerFactoryBean){
log.info("注入SchedulerFactoryBean...");
SchedulerFactoryBean factoryBean = new SchedulerFactoryBean();
CronTrigger[] triggers = new CronTrigger[cronTriggerFactoryBean.length];
for(int i = 0; i < cronTriggerFactoryBean.length; i++){
triggers[i] = cronTriggerFactoryBean[i].getObject();
}
//注册触发器,一个Scheduler可以注册若干触发器
factoryBean.setTriggers(triggers);
//为Scheduler设置JobDetail的工厂。可以覆盖掉SpringBoot提供的默认工厂,保证JobDetail中的自动装配有效。
factoryBean.setJobFactory(quartzTaskFactory);
return factoryBean;
}
}