什么是Quartz?
Quartz是OpenSymphony开源组织在Job scheduling领域又一个开源项目,完全由Java开发,可以用来执行定时任务,类似于java.util.Timer。但是相较于Timer, Quartz增加了很多功能:
持久性作业 - 就是保持调度定时的状态;
作业管理 - 对调度作业进行有效的管理;
ps: 大部分公司都会用到定时任务这个功能。
拿火车票购票来说,当你下单后,后台就会插入一条待支付的task(job),一般是30分钟,超过30min后就会执行这个job,去判断你是否支付,未支付就会取消此次订单;当你支付完成之后,后台拿到支付回调后就会再插入一条待消费的task(job),Job触发日期为火车票上的出发日期,超过这个时间就会执行这个job,判断是否使用等。
在我们实际的项目中,当Job过多的时候,肯定不能人工去操作,这时候就需要一个任务调度框架,帮我们自动去执行这些程序。那么该如何实现这个功能呢?
(1)首先我们需要定义实现一个定时功能的接口,我们可以称之为Task(或Job),如定时发送邮件的task(Job),重启机器的task(Job),优惠券到期发送短信提醒的task(Job),实现接口如下:
(2)有了任务之后,还需要一个能够实现触发任务去执行的触发器,触发器Trigger最基本的功能是指定Job的执行时间,执行间隔,运行次数等。
(3)有了Job和Trigger后,怎么样将两者结合起来呢?即怎样指定Trigger去执行指定的Job呢?这时需要一个Schedule,来负责这个功能的实现。
上面三个部分就是Quartz的基本组成部分:
- 调度器:Scheduler
- 任务:JobDetail
- 触发器:Trigger,包括SimpleTrigger和CronTrigger
那么明白 Quartz了 的几个核心概念,这样理解起 Quartz 的原理就会变得简单了。
- Job 表示一个工作,要执行的具体内容。类似于TimerTask类。需要实现方法 void execute(JobExecutionContext context)
- JobDetail 表示一个具体的可执行的调度程序,Job 是这个可执行程调度程序所要执行的内容,另外 JobDetail 还包含了这个任务调度的方案和策略。
- Trigger 代表一个调度参数的配置,什么时候去调。
- Scheduler 代表一个调度容器。类似于Timer类。一个调度容器中可以注册多个 JobDetail 和 Trigger。当 Trigger 与 JobDetail 组合,就可以被 Scheduler 容器调用。
- JobListener: 任务执行的监听器,可以监听到任务执行前、后以及未能成功执行抛出异常。
1. 第一种集成 Springboot项目启动就执行SpringBoot集成Quartz依然沿用了Spring的典型方式,使用工厂Bean生成Bean的方式。在Quartz中,需要被调度的任务叫做Job,而负责调度任务则是Scheduler。
我们首先需要配置工厂Bean:JobFactory接口,自定义一个AutowiringSpringBeanJobFactory类继承SpringBeanJobFactory(实现了JobFactory接口)AutowiringSpringBeanJobFactory工厂类将负责生成实现了Job接口的类的实例对象Bean。
org.quartz-scheduler quartz 2.2.1
快速入门,2步搞定 1.启动类上面加 @EnableScheduling 2.方法上面加上@Scheduled(cron = "0/3 * * * * ? ") 每3秒执行一次
cron资料: https://www.cnblogs.com/javahr/p/8318728.html
然后就可以启动看结果了!!!
2. 第二种动态开启定时任务 1.引入依赖(Springboot项目)2.配置文件可选配置org.springframework.boot spring-boot-starter org.projectlombok lombok true org.springframework.boot spring-boot-starter-test test mysql mysql-connector-java 8.0.26 org.mybatis.spring.boot mybatis-spring-boot-starter 1.3.2 org.springframework.boot spring-boot-starter-jdbc org.springframework.boot spring-boot-starter-web org.quartz-scheduler quartz 2.2.1 slf4j-api org.slf4j
#是否开启监听器 -> 可以不写,默认为不开启 jobListener: switch: true3.创建一个执行任务的job类
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class helloJob implements Job{
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
// 取出JobDataMap中的数据
System.out.println(jobExecutionContext.getJobDetail().getJobDataMap().get("jobDetail1"));
System.out.println(jobExecutionContext.getTrigger().getJobDataMap().get("trigger1"));
System.out.println("定时任务执行啦:"+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
}
}
3.创建JobListener监听器
package com.demo.quartz.config;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.JobListener;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
@Component
public class SchedulerListener implements JobListener {
public static final String LISTENER_NAME = "QuartSchedulerListener";
@Override
public String getName() {
return LISTENER_NAME; //must return a name
}
//任务被调度前
@Override
public void jobToBeExecuted(JobExecutionContext context) {
String jobName = context.getJobDetail().getKey().toString();
System.out.println("任务被调度前");
System.out.println("Job : " + jobName + " is going to start...");
}
//任务调度被拒了
@Override
public void jobExecutionVetoed(JobExecutionContext context) {
System.out.println("任务调度被拒了");
//可以做一些日志记录原因
}
//任务被调度后
@Override
public void jobWasExecuted(JobExecutionContext context,
JobExecutionException jobException) {
System.out.println("任务被调度后");
String jobName = context.getJobDetail().getKey().toString();
System.out.println("Job : " + jobName + " is finished...");
if (jobException!=null&&!jobException.getMessage().equals("")) {
System.out.println("Exception thrown by: " + jobName
+ " Exception: " + jobException.getMessage());
}
}
}
4.创建SchedulerConfig配置类
package com.demo.quartz.config;
import lombok.SneakyThrows;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import org.quartz.spi.JobFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.PropertiesFactoryBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.scheduling.quartz.CronTriggerFactoryBean;
import org.springframework.scheduling.quartz.JobDetailFactoryBean;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import java.io.IOException;
import java.util.Properties;
@Configuration
public class SchedulerConfig {
@Autowired
private SchedulerListener scheduleListener;
// 是否开启监听器 默认为false
@Value("${jobListener.switch:false}")
private boolean jobListener;
@Bean
public SchedulerFactory schedulerFactoryBean(){
return new StdSchedulerFactory();
}
@SneakyThrows
@Bean
public Scheduler scheduler(@Qualifier("schedulerFactoryBean") SchedulerFactory schedulerFactoryBean){
Scheduler scheduler = schedulerFactoryBean.getScheduler();
if(jobListener){
scheduler.getListenerManager().addJobListener(scheduleListener);
}
return scheduler;
}
}
5.自定义接口请求 执行定时任务
import com.demo.quartz.config.helloJob;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.concurrent.TimeUnit;
@RestController
public class JobController {
// 调度器
@Autowired
private Scheduler scheduler;
// @Scheduled(cron = "0/3 * * * * ? ")
public void text(){
//每3秒执行一次
System.out.println("定时任务执行啦:"+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
}
@PostMapping(value = "testTask1")
public void testTask1( )throws SchedulerException {
//可用于传值给job 具体看helloJob类如何取值使用
JobDataMap jobDataMap = new JobDataMap();
jobDataMap.put("jobDetail1","jobDetail1");
JobDataMap jobDataMap2 = new JobDataMap();
jobDataMap2.put("trigger1","这是jobDetail1的trigger");
// 1、创建JobDetail实例,并与PrintWordsJob类绑定(Job执行内容)
JobDetail jobDetail = JobBuilder.newJob(helloJob.class).usingJobData(jobDataMap)
.withIdentity("job1", "group1").build();
// 2、构建Trigger(触发器)实例,每隔1s执行一次
Trigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger1", "triggerGroup1")
.usingJobData(jobDataMap2) //存值到jobDataMap Job类中能取出
// 可设置触发时间
.startNow()//立即生效
.withSchedule(SimpleScheduleBuilder.simpleSchedule()
.withIntervalInSeconds(1)//每隔1s执行一次
.repeatForever()).build();//一直执行
//3、执行
scheduler.scheduleJob(jobDetail, trigger);
scheduler.start();
}
@PostMapping(value = "testTask2")
public void testTask2( )throws SchedulerException {
// 获取一周后的日期
Date endDate = new Date();
Calendar now = Calendar.getInstance();
now.add(Calendar.DATE, +7);
Date endAt = now.getTime();
//可用于传值给job
JobDataMap jobDataMap = new JobDataMap();
jobDataMap.put("jobDetail1","jobDetail1");
JobDataMap jobDataMap2 = new JobDataMap();
jobDataMap2.put("trigger1","这是jobDetail1的trigger");
// 1、创建JobDetail实例,并与PrintWordsJob类绑定(Job执行内容)
JobDetail jobDetail = JobBuilder.newJob(helloJob.class).usingJobData(jobDataMap)
.withIdentity("job1", "group1").build();
// 2、构建Trigger实例,每隔1s执行一次
Trigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger1", "triggerGroup1")
.usingJobData(jobDataMap2) //存值到jobDataMap
.startNow() // 立即触发
.endAt(endAt) // 表示该任务在一周后自动停止 ps:表示触发器结束触发的时间;
//使用cron表达式 每天中午8点触发
.withSchedule(CronScheduleBuilder.cronSchedule("0 0 8 * * ?"))
.build();
//3、执行
scheduler.scheduleJob(jobDetail, trigger);
scheduler.start();
}
@PostMapping(value = "testTask3")
public void testTask3( )throws SchedulerException {
Calendar now = Calendar.getInstance();
now.setTime(new Date());
// 获取一周后的日期
now.add(Calendar.DATE, +7);
Date endAt = now.getTime();
// 获取一天后的日期
now.add(Calendar.DATE, +1);
Date startAt = now.getTime();
//可用于传值给job
JobDataMap jobDataMap = new JobDataMap();
jobDataMap.put("jobDetail1","jobDetail1");
JobDataMap jobDataMap2 = new JobDataMap();
jobDataMap2.put("trigger1","这是jobDetail1的trigger");
// 1、创建JobDetail实例,并与PrintWordsJob类绑定(Job执行内容)
JobDetail jobDetail = JobBuilder.newJob(helloJob.class).usingJobData(jobDataMap)
.withIdentity("job1", "group1").build();
// 2、构建Trigger实例,每隔1s执行一次
Trigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger1", "triggerGroup1")
.usingJobData(jobDataMap2) //存值到jobDataMap
.startAt(startAt) // 立即触发
.endAt(endAt) // 表示该任务在一周后自动停止 ps:表示触发器结束触发的时间;
//使用cron表达式 每天中午8点触发
.withSchedule(CronScheduleBuilder.cronSchedule("0 0 8 * * ?"))
.build();
//3、执行
scheduler.scheduleJob(jobDetail, trigger);
scheduler.start();
}
@RequestMapping("/deleteJob")
public void deleteJob(String jobName,String jobGroup) throws SchedulerException
{
JobKey jobKey=new JobKey(jobName,jobGroup);
scheduler.deleteJob(jobKey);
}
@RequestMapping("/pauseJob")
public void pauseJob(String jobName,String jobGroup) throws SchedulerException
{
JobKey jobKey=new JobKey(jobName,jobGroup);
scheduler.pauseJob(jobKey);
}
@RequestMapping("/resumeJob")
public void resumeJob(String jobName,String jobGroup) throws SchedulerException
{
JobKey triggerKey=new JobKey(jobName,jobGroup);
scheduler.resumeJob(triggerKey);
}
@RequestMapping("/clearAll")
public void clearAll() throws SchedulerException {
scheduler.clear();
}
}
6.自定义工具类
package com.demo.quartz.utils;
import org.quartz.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.stereotype.Component;
@Component
public class SchedulerUtils {
// 调度器
@Autowired
private Scheduler scheduler;
public void startJob(String cron,String jobName,String jobGroup,Class extends Job> jobClass) throws SchedulerException
{
JobKey jobKey=new JobKey(jobName,jobGroup);
//判断是否存在相同的计划任务
if(!scheduler.checkExists(jobKey))
{
scheduleJob(cron,scheduler,jobName,jobGroup,jobClass);
}
}
public void deleteJob(String jobName,String jobGroup) throws SchedulerException
{
JobKey jobKey=new JobKey(jobName,jobGroup);
scheduler.deleteJob(jobKey);
}
public void pauseJob(String jobName,String jobGroup) throws SchedulerException
{
JobKey jobKey=new JobKey(jobName,jobGroup);
scheduler.pauseJob(jobKey);
}
public void resumeJob(String jobName,String jobGroup) throws SchedulerException
{
JobKey triggerKey=new JobKey(jobName,jobGroup);
scheduler.resumeJob(triggerKey);
}
public void clearAll() throws SchedulerException {
scheduler.clear();
}
private void scheduleJob(String cron,Scheduler scheduler,String jobName,String jobGroup,Class extends Job> jobClass) throws SchedulerException{
JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(jobName, jobGroup).build();
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cron);
CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity(jobName, jobGroup).withSchedule(scheduleBuilder).build();
scheduler.scheduleJob(jobDetail,cronTrigger);
}
}
总结:
-
新建一个类实现Job并是实现void execute(JobExecutionContext var1) throws JobExecutionException;
-
创建JobDetail,封装Job,它是Scheduler(调度器)真正调度的对象,可以设置Job名称,组等信息,且名称不能重复
-
定义触发器Trigger,代表一个调度参数的配置,什么时候去调JobDetail,
-
创建就Scheduler,代表一个调度器 类似于Timer类。一个调度容器中可以注册多个 JobDetail 和 Trigger。当 Trigger 与 JobDetail 组合,就可以被 Scheduler 容器调用。
-
最后将jobDetail和 Trigger添加到Scheduler中,并启动
scheduler.scheduleJob(jobDetail, trigger);
scheduler.start();
代码已上到gitee: https://gitee.com/Lazy_001/quartz.git



