1、简介
Spring Boot 内置注解方式实现的定时任务,在一定程度上也能解决一定的业务场景问题,但是若做更复杂的动作,例如启停任务、删除任务等等操作,实现起来则稍显复杂,此时便可以通过集成开源任务框架来实现。
常见的定时任务框架有 Quartz、elastic-job、xxl-job等等。
Quartz 存储方式有两种:MEMORY 和 JDBC。默认是内存形式维护任务信息,意味着服务重启了任务就从头再来,就像喝酒断片了一样;而 JDBC 形式就是能够把任务信息持久化到数据库,虽然服务重启了,依然还能接着来。
Quartz 提供了单机版和集群版,默认就是单机版。
2、使用内存方式
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- 引入 Quartz 依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
2.1、定义任务类
可以通过实现 Job 接口来定义任务,也可以通过继承 QuartzJobBean 这个抽象类来定义任务,其实 QuartzJobBean 本身也实现了 Job 接口,其本质都是实现 Job 接口来定义任务。
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.scheduling.quartz.QuartzJobBean;
/**
* QuartzJobBean
* Job 的实例要到该执行它们的时候才会实例化出来。每次 Job 被执行,一个新的 Job 实例会被创建。
* 其中暗含的意思就是你的 Job 不必担心线程安全性,因为同一时刻仅有一个线程去执行给定 Job 类的实例,甚至是并发执行同一 Job 也是如此。
* @DisallowConcurrentExecution 保证上一个任务执行完后,再去执行下一个任务
*/
/**
* 定义任务方式一
*/
public class HelloJob1 extends QuartzJobBean {
private static final Log logger = LogFactory.getLog(HelloJob1.class);
@Override
protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
logger.info("HelloJob1*************************************");
}
}
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import java.io.Serializable;
/**
* 定义任务方式二
*/
public class HelloJob2 implements Job, Serializable {
private static final Log logger = LogFactory.getLog(HelloJob2.class);
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
logger.info("HelloJob2*************************************");
}
}
2.2、 定义任务描述及任务触发规则
import org.quartz.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import top.lrshuai.timer.job.HelloJob1;
@Configuration
public class QuartzConfig {
@Bean
public JobDetail jobDetail() {
//指定任务描述具体的实现类
return JobBuilder.newJob(HelloJob1.class)
// 指定任务的名称
.withIdentity("HelloJob1")
// 任务描述
.withDescription("任务描述:测试1")
// 每次任务执行后进行存储
.storeDurably()
.build();
}
@Bean
public Trigger trigger() {
//创建触发器
return TriggerBuilder.newTrigger()
## // 绑定工作任务
.forJob(jobDetail())
// 每隔 5 秒执行一次 job 固定间隔方式
.withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(5))
//cron表达式方式
//.withSchedule(CronScheduleBuilder.cronSchedule("*/5 * * * * ?"))
.build();
}
}
3、 数据库方式存储任务信息
3.1、引入依赖
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.4</version>
</dependency>
<!-- MySQL 驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
3.2、添加 Quartz 配置信息
spring:
datasource:
quartz:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/quartz?serverTimezone=GMT%2B8
username: root
password: 123456
quartz:
job-store-type: jdbc # 使用数据库存储
scheduler-name: hyhScheduler # 相同 Scheduler 名字的节点,形成一个 Quartz 集群
wait-for-jobs-to-complete-on-shutdown: true # 应用关闭时,是否等待定时任务执行完成。默认为 false ,建议设置为 true
jdbc:
initialize-schema: never # 是否自动使用 SQL 初始化 Quartz 表结构。这里设置成 never ,我们手动创建表结构。
properties:
org:
quartz:
# JobStore 相关配置
jobStore:
dataSource: quartzDataSource # 使用的数据源
class: org.quartz.impl.jdbcjobstore.JobStoreTX # JobStore 实现类
driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate
tablePrefix: QRTZ_ # Quartz 表前缀
isClustered: true # 是集群模式
clusterCheckinInterval: 1000
useProperties: false
# 线程池相关配置
threadPool:
threadCount: 25 # 线程池大小。默认为 10 。
threadPriority: 5 # 线程优先级
class: org.quartz.simpl.SimpleThreadPool # 线程池类型
spring:
# 将 Quartz 持久化方式修改为 jdbc
quartz:
job-store-type: jdbc
datasource:
sql-script-encoding: UTF-8
driverClassName: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/quartz?useUnicode=true&characterEncoding=utf-8&useSSL=false&autoReconnect=true&serverTimezone=Hongkong
username: root
password: 123456
3.3 初始化 Quartz 数据表信息
3.4、创建任务
@Component
public class JobInit implements ApplicationRunner {
private static final String ID = "SUMMERDAY";
@Autowired
private Scheduler scheduler;
@Override
public void run(ApplicationArguments args) throws Exception {
JobDetail jobDetail = JobBuilder.newJob(SecondJob.class)
.withIdentity(ID + " 02")
.storeDurably()
.build();
CronScheduleBuilder scheduleBuilder =
CronScheduleBuilder.cronSchedule("0/10 * * * * ? *");
// 创建任务触发器
Trigger trigger = TriggerBuilder.newTrigger()
.forJob(jobDetail)
.withIdentity(ID + " 02Trigger")
.withSchedule(scheduleBuilder)
.startNow() //立即執行一次任務
.build();
Set<Trigger> set = new HashSet<>();
set.add(trigger);
// boolean replace 表示启动时对数据库中的quartz的任务进行覆盖。
scheduler.scheduleJob(jobDetail, set, true);
}
}
4、任务动态管理
4.1、实体类配置
Quartz
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("task_quartz")
public class Quartz implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "id", type = IdType.AUTO)
private Long id;
/**
* 任务分组
*/
private String jobGroup;
/**
* 任务名
*/
private String jobName;
/**
* 任务描述
*/
private String description;
/**
* cron表达式
*/
private String cronExpression;
/**
* 任务执行时调用哪个类的方法 包名+类名
*/
private String jobClassName;
/**
* 任务状态
*/
private String jobStatus;
/**
* 创建者
*/
private String createBy;
/**
* 创建时间
*/
private LocalDateTime createTime;
/**
* 更新者
*/
private String modifyBy;
/**
* 更新时间
*/
private LocalDateTime modifyTime;
@TableField(exist = false)
private String oldJobName;//任务名称 用于修改
@TableField(exist = false)
private String oldJobGroup;//任务分组 用于修改
}
JobStatus
public enum JobStatus {
/**
* STATE_BLOCKED("4"),
*/
RUN("1"),
PAUSED("2"),
STOP("3");
private String status;
private JobStatus(String status){
this.status=status;
}
public String getStatus() {
return status;
}
}
4.2、定时任务请求
QuartzController
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.quartz.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import top.lrshuai.timer.common.constant.ApiResultEnum;
import top.lrshuai.timer.common.constant.Result;
import top.lrshuai.timer.task.entity.JobStatus;
import top.lrshuai.timer.task.entity.Quartz;
import top.lrshuai.timer.task.entity.QuartzDTO;
import top.lrshuai.timer.task.service.IQuartzService;
import javax.servlet.http.HttpServletResponse;
import java.time.LocalDateTime;
import java.util.List;
/**
* <p>
* 前端控制器
* </p>
*
* @author rstyro
* @since 2019-02-26
*/
@RestController
@RequestMapping("/task/quartz")
public class QuartzController {
private Logger LOGGER = LoggerFactory.getLogger(this.getClass());
@Autowired
private Scheduler scheduler;
@Autowired
private IQuartzService quartzService;
@PostMapping("/add")
public Result save(Quartz quartz){
LOGGER.info("新增任务");
try {
List<Quartz> list = quartzService.list(new QueryWrapper<Quartz>().lambda().eq(Quartz::getJobGroup, quartz.getJobGroup()).eq(Quartz::getJobName, quartz.getJobName()));
if(list != null && list.size() > 0){
return Result.error(ApiResultEnum.TRIGGER_GROUP_AND_NAME_SAME);
}
System.out.println("Quartz="+ JSON.toJSONString(quartz));
quartz.setCreateTime(LocalDateTime.now());
quartz.setJobStatus(JobStatus.STOP.getStatus());
quartzService.save(quartz);
} catch (Exception e) {
e.printStackTrace();
return Result.error();
}
return Result.ok();
}
@GetMapping("/list")
public Result list(QuartzDTO dto){
LOGGER.info("任务列表");
return Result.ok(quartzService.getQuartzPage(dto));
}
@PostMapping("/start")
public Result start(Long id) throws Exception {
LOGGER.info("任务列表");
Quartz quartz = quartzService.getById(id);
if(quartz.getJobStatus().equals(JobStatus.RUN.getStatus())){
return Result.error(ApiResultEnum.TASK_IS_RUNING);
}else if(quartz.getJobStatus().equals(JobStatus.PAUSED.getStatus())){
return Result.error(ApiResultEnum.TASK_IS_PAUSE);
}
addJob(quartz);
quartz.setJobStatus(JobStatus.RUN.getStatus());
quartzService.updateById(quartz);
return Result.ok();
}
/**
* 启动任务
* @param quartz
* @throws Exception
*/
public void addJob(Quartz quartz) throws Exception {
Class cls = Class.forName(quartz.getJobClassName()) ;
cls.newInstance();
//构建job信息
JobDetail job = JobBuilder.newJob(cls).withIdentity(quartz.getJobName(),quartz.getJobGroup()).withDescription(quartz.getDescription()).build();
// 触发时间点
CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(quartz.getCronExpression());
Trigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger_"+quartz.getJobName(), quartz.getJobGroup()).withDescription(quartz.getDescription()).startNow().withSchedule(cronScheduleBuilder).build();
//交由Scheduler安排触发
scheduler.scheduleJob(job, trigger);
}
/**
* 编辑
* @param quartz
* @return
* @throws SchedulerException
*/
@PostMapping("/edit")
public Result edit(Quartz quartz) throws SchedulerException {
LOGGER.info("任务列表");
//如果是修改 展示旧的 任务
if(quartz.getOldJobGroup()!=null){
TriggerKey triggerKey = TriggerKey.triggerKey(quartz.getOldJobName(), quartz.getOldJobGroup());
// 停止触发器
scheduler.pauseTrigger(triggerKey);
// 移除触发器
scheduler.unscheduleJob(triggerKey);
JobKey key = new JobKey(quartz.getOldJobName(),quartz.getOldJobGroup());
scheduler.deleteJob(key);
System.out.println("移除任务:"+JSON.toJSONString(key));
}
quartz.setJobStatus(JobStatus.STOP.getStatus());
quartz.setModifyTime(LocalDateTime.now());
quartzService.updateById(quartz);
return Result.ok();
}
@GetMapping(value="/query")
@ResponseBody
public Result query(Long id) throws Exception {
return quartzService.getDetail(id);
}
@PostMapping("/trigger")
public Result trigger(Quartz quartz, HttpServletResponse response) {
LOGGER.info("触发任务");
try {
JobKey key = new JobKey(quartz.getJobName(),quartz.getJobGroup());
scheduler.triggerJob(key);
} catch (SchedulerException e) {
e.printStackTrace();
return Result.error();
}
return Result.ok();
}
@PostMapping("/pause")
public Result pause(Long id) {
LOGGER.info("停止任务");
try {
Quartz quartz = quartzService.getById(id);
if(quartz == null){
return Result.error("操作异常");
}
if(JobStatus.PAUSED.getStatus().equals(quartz.getJobStatus())){
//停止则恢复
JobKey key = new JobKey(quartz.getJobName(),quartz.getJobGroup());
scheduler.resumeJob(key);
quartz.setJobStatus(JobStatus.RUN.getStatus());
}else if(JobStatus.RUN.getStatus().equals(quartz.getJobStatus())){
JobKey key = new JobKey(quartz.getJobName(),quartz.getJobGroup());
scheduler.pauseJob(key);
quartz.setJobStatus(JobStatus.PAUSED.getStatus());
}else{
return Result.error(ApiResultEnum.TASK_NOT_RUNING);
}
quartzService.updateById(quartz);
} catch (SchedulerException e) {
e.printStackTrace();
return Result.error();
}
return Result.ok();
}
@PostMapping("/remove")
public Result remove(Long id) {
LOGGER.info("移除任务");
try {
Quartz quartz = quartzService.getById(id);
if(quartz == null){
return Result.error("操作异常");
}
TriggerKey triggerKey = TriggerKey.triggerKey(quartz.getJobName(), quartz.getJobGroup());
// 停止触发器
scheduler.pauseTrigger(triggerKey);
// 移除触发器
scheduler.unscheduleJob(triggerKey);
// 删除任务
scheduler.deleteJob(JobKey.jobKey(quartz.getJobName(), quartz.getJobGroup()));
System.out.println("removeJob:"+JobKey.jobKey(quartz.getJobName()));
quartzService.removeById(id);
} catch (Exception e) {
e.printStackTrace();
return Result.error();
}
return Result.ok();
}
}
5、JobListener监听器
JobListener
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.JobListener;
public class MyJobListener implements JobListener {
@Override
public String getName() {
String name = this.getClass().getSimpleName();
System.out.println("监听器的名称是:" +name);
return name;
}
@Override
public void jobToBeExecuted(JobExecutionContext context) {
String name = context.getJobDetail().getKey().getName();
System.out.println("Job的名称是:" +name + " Scheduler在JobDetail将要被执行时调用的方法");
}
@Override
public void jobExecutionVetoed(JobExecutionContext context) {
String name = context.getJobDetail().getKey().getName();
System.out.println("Job的名称是:" +name + " Scheduler在JobDetail即将被执行,但又被TriggerListener否决时会调用该方法");
}
@Override
public void jobWasExecuted(JobExecutionContext context, JobExecutionException jobException) {
String name = context.getJobDetail().getKey().getName();
System.out.println("Job的名称是:" +name + " Scheduler在JobDetail被执行之后调用这个方法");
}
}
SchedulerListener
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.SchedulerException;
import org.quartz.SchedulerListener;
import org.quartz.Trigger;
import org.quartz.TriggerKey;
public class MySchedulerListener implements SchedulerListener {
@Override
public void jobScheduled(Trigger trigger) {
String name = trigger.getKey().getName();
// 用于部署JobDetail时调用
System.out.println(name +" 完成部署");
}
@Override
public void jobUnscheduled(TriggerKey triggerKey) {
String name = triggerKey.getName();
// 用于卸载JobDetail时调用
System.out.println(name +" 完成卸载");
}
@Override
public void triggerFinalized(Trigger trigger) {
String name = trigger.getKey().getName();
// 当一个Trigger来到了再也不会触发的状态时调用这个方法。除非这个Job已设置成了持久性,否则它就会从Scheduler中移除。
System.out.println(name +" 触发器被移除");
}
@Override
public void triggerPaused(TriggerKey triggerKey) {
String name = triggerKey.getName();
// Scheduler调用这个方法是发生在一个Trigger或Trigger组被暂停时。假如是Trigger组的话,triggerName参数将为null。
System.out.println(name +" 正在被暂停");
}
@Override
public void triggersPaused(String triggerGroup) {
// Scheduler调用这个方法是发生在一个Trigger或Trigger组被暂停时。假如是Trigger组的话,triggerName参数将为null。
System.out.println("触发器组" +triggerGroup +" 正在被暂停");
}
@Override
public void triggerResumed(TriggerKey triggerKey) {
// Scheduler调用这个方法是发生在一个Trigger或Trigger组从暂停中恢复时。假如是Trigger组的话,triggerName参数将为null。参数将为null。
String name = triggerKey.getName();
System.out.println(name +" 正在从暂停中恢复");
}
@Override
public void triggersResumed(String triggerGroup) {
// Scheduler调用这个方法是发生在一个Trigger或Trigger组从暂停中恢复时。假如是Trigger组的话,triggerName参数将为null。参数将为null。
System.out.println("触发器组" +triggerGroup +" 正在从暂停中恢复");
}
@Override
public void jobAdded(JobDetail jobDetail) {
//
System.out.println(jobDetail.getKey() +" 添加工作任务");
}
@Override
public void jobDeleted(JobKey jobKey) {
//
System.out.println(jobKey +" 删除工作任务");
}
@Override
public void jobPaused(JobKey jobKey) {
//
System.out.println(jobKey +" 工作任务正在被暂停");
}
@Override
public void jobsPaused(String jobGroup) {
//
System.out.println("工作组" +jobGroup +" 正在被暂停");
}
@Override
public void jobResumed(JobKey jobKey) {
//
System.out.println(jobKey +" 正在从暂停中恢复");
}
@Override
public void jobsResumed(String jobGroup) {
//
System.out.println("工作组" +jobGroup +" 正在从暂停中恢复");
}
@Override
public void schedulerError(String msg, SchedulerException cause) {
// 在Scheduler的正常运行期间产生一个严重错误时调用这个方法。
System.out.println("产生严重错误的时候调用" +msg +" " +cause.getUnderlyingException());
}
@Override
public void schedulerInStandbyMode() {
// 当Scheduler处于StandBy模式时,调用该方法。
System.out.println("调度器被挂起模式的时候调用");
}
@Override
public void schedulerStarted() {
// 当Scheduler开启时,调用该方法
System.out.println("调度器开启的时候调用");
}
@Override
public void schedulerStarting() {
//
System.out.println("调度器正在开启的时候调用");
}
@Override
public void schedulerShutdown() {
//
System.out.println("调度器关闭的时候调用");
}
@Override
public void schedulerShuttingdown() {
//
System.out.println("调度器正在关闭的时候调用");
}
@Override
public void schedulingDataCleared() {
// 当Scheduler中的数据被清除时,调用该方法
System.out.println("调度器数据被清除的时候调用");
}
}
TriggerListener
import org.quartz.JobExecutionContext;
import org.quartz.Trigger;
import org.quartz.Trigger.CompletedExecutionInstruction;
import org.quartz.TriggerListener;
public class MyTriggerListener implements TriggerListener {
// private String name;
// // 构造方法,自定义传递触发器的名称,默认是类的名称
// public MyTriggerListener(String name) {
// super();
// this.name = name;
// }
// @Override
// public String getName() {
// return this.name; // 不返还会抛出一个名称为空的异常
// }
@Override
public String getName() {
String name = this.getClass().getSimpleName();
System.out.println("触发器的名称:" +name);
return name; // 不返还会抛出一个名称为空的异常
}
@Override
public void triggerFired(Trigger trigger, JobExecutionContext context) {
String name = this.getClass().getSimpleName();
System.out.println(name +"被触发");
}
@Override
public boolean vetoJobExecution(Trigger trigger, JobExecutionContext context) {
String name = this.getClass().getSimpleName();
// TriggerListener给了一个选择去否决Job的执行。假如这个方法返回true,这个Job将不会为此次Trigger触发而得到执行。
System.out.println(name +" 没有被触发");
return false; // true:表示不会执行Job的方法
}
@Override
public void triggerMisfired(Trigger trigger) {
String name = this.getClass().getSimpleName();
// Scheduler调用这个方法是在Trigger错过触发时
System.out.println(name +" 错过触发");
}
@Override
public void triggerComplete(Trigger trigger, JobExecutionContext context,
CompletedExecutionInstruction triggerInstructionCode) {
String name = this.getClass().getSimpleName();
// Trigger被触发并且完成了Job的执行时,Scheduler调用这个方法。
System.out.println(name +" 完成之后触发");
}
}
public static void main(String[] args) throws Exception {
// 1、调度器(Scheduler),从工厂中获取调度的实例(默认:实例化new StdSchedulerFactory();)
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
// 2、任务实例(JobDetail)定义一个任务调度实例,将该实例与HelloJobSimpleTrigger绑定,任务类需要实现Job接口
JobDetail jobDetail = JobBuilder.newJob(HelloJobListener.class) // 加载任务类,与HelloJob完成绑定,要求HelloJob实现Job接口
.withIdentity("job1", "group1") // 参数1:任务的名称(唯一实例);参数2:任务组的名称
.usingJobData("message", "打印日志") // 传递参数,名称message
.usingJobData("count", 0)
.build();
System.out.println("name:" +jobDetail.getKey().getName());
System.out.println("group:" +jobDetail.getKey().getGroup()); // 如果没有指定组名:DEFAULT
System.out.println("jobClass:" +jobDetail.getJobClass().getName());
// 3、触发器(Trigger)定义触发器,马上执行,然后每5秒重复执行一次
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("trigger1", "group1") // 参数1:触发器的名称(唯一实例);参数2:触发器组的名称
.startNow()
.withSchedule(SimpleScheduleBuilder.simpleSchedule().repeatSecondlyForever(5).withRepeatCount(2)) // 每5秒执行一次,连续执行3次后停止,默认是0
.build();
// 4、让调度器关联任务和触发器,保证按照触发器定义的调整执行任务
scheduler.scheduleJob(jobDetail, trigger);
// 创建调度器的监听
scheduler.getListenerManager().addSchedulerListener(new MySchedulerListener());
// 移除对应的调度器的监听
// scheduler.getListenerManager().removeSchedulerListener(new MySchedulerListener());
// 创建并注册一个全局的Job Listener
//scheduler.getListenerManager().addJobListener(new MyJobListener(), EverythingMatcher.allJobs());
// 创建并注册一个局部的Job Listener,表示指定的任务Job
//scheduler.getListenerManager().addJobListener(new MyJobListener(), KeyMatcher.keyEquals(JobKey.jobKey("job1", "group1")));
// 创建并注册一个全局的Trigger Listener
// scheduler.getListenerManager().addTriggerListener(new MyTriggerListener(), EverythingMatcher.allTriggers());
// 创建并注册一个局部的Trigger Listener
//scheduler.getListenerManager().addTriggerListener(new MyTriggerListener(), KeyMatcher.keyEquals(TriggerKey.triggerKey("trigger1", "group1")));
// 5、启动
scheduler.start();
/**
* shutdown(true):表示等待所有正在执行的Job执行完毕之后,再关闭Scheduler
* shutdown(false): 表示直接关闭Scheduler
*/
//scheduler.shutdown(false);
// 挂起
//scheduler.standby();
// 线程延迟7秒后关闭
Thread.sleep(7000L);
// 关闭
scheduler.shutdown();
}
参考代码:动态定时任务
import lombok.extern.slf4j.Slf4j;
import name.ealen.domain.entity.TaskDefine;
import org.quartz.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.stereotype.Service;
import java.util.Map;
/**
* Created by EalenXie on 2019/7/10 13:49.
* 核心其实就是Scheduler的功能 , 这里只是非常简单的示例说明其功能
* 如需根据自身业务进行扩展 请参考 {@link org.quartz.Scheduler}
*/
@Slf4j
@Service
public class QuartzJobService {
//Quartz定时任务核心的功能实现类
private Scheduler scheduler;
public QuartzJobService(@Autowired SchedulerFactoryBean schedulerFactoryBean) {
scheduler = schedulerFactoryBean.getScheduler();
}
/**
* 创建和启动 定时任务
* {@link org.quartz.Scheduler#scheduleJob(JobDetail, Trigger)}
*
* @param define 定时任务
*/
public void scheduleJob(TaskDefine define) throws SchedulerException {
//1.定时任务 的 名字和组名
JobKey jobKey = define.getJobKey();
//2.定时任务 的 元数据
JobDataMap jobDataMap = getJobDataMap(define.getJobDataMap());
//3.定时任务 的 描述
String description = define.getDescription();
//4.定时任务 的 逻辑实现类
Class<? extends Job> jobClass = define.getJobClass();
//5.定时任务 的 cron表达式
String cron = define.getCronExpression();
JobDetail jobDetail = getJobDetail(jobKey, description, jobDataMap, jobClass);
Trigger trigger = getTrigger(jobKey, description, jobDataMap, cron);
scheduler.scheduleJob(jobDetail, trigger);
}
/**
* 暂停Job
* {@link org.quartz.Scheduler#pauseJob(JobKey)}
*/
public void pauseJob(JobKey jobKey) throws SchedulerException {
scheduler.pauseJob(jobKey);
}
/**
* 恢复Job
* {@link org.quartz.Scheduler#resumeJob(JobKey)}
*/
public void resumeJob(JobKey jobKey) throws SchedulerException {
scheduler.resumeJob(jobKey);
}
/**
* 删除Job
* {@link org.quartz.Scheduler#deleteJob(JobKey)}
*/
public void deleteJob(JobKey jobKey) throws SchedulerException {
scheduler.deleteJob(jobKey);
}
/**
* 修改Job 的cron表达式
*/
public boolean modifyJobCron(TaskDefine define) {
String cronExpression = define.getCronExpression();
//1.如果cron表达式的格式不正确,则返回修改失败
if (!CronExpression.isValidExpression(cronExpression)) return false;
JobKey jobKey = define.getJobKey();
TriggerKey triggerKey = new TriggerKey(jobKey.getName(), jobKey.getGroup());
try {
CronTrigger cronTrigger = (CronTrigger) scheduler.getTrigger(triggerKey);
JobDataMap jobDataMap = getJobDataMap(define.getJobDataMap());
//2.如果cron发生变化了,则按新cron触发 进行重新启动定时任务
if (!cronTrigger.getCronExpression().equalsIgnoreCase(cronExpression)) {
CronTrigger trigger = TriggerBuilder.newTrigger()
.withIdentity(triggerKey)
.withSchedule(CronScheduleBuilder.cronSchedule(cronExpression))
.usingJobData(jobDataMap)
.build();
scheduler.rescheduleJob(triggerKey, trigger);
}
} catch (SchedulerException e) {
log.error("printStackTrace", e);
return false;
}
return true;
}
/**
* 获取定时任务的定义
* JobDetail是任务的定义,Job是任务的执行逻辑
*
* @param jobKey 定时任务的名称 组名
* @param description 定时任务的 描述
* @param jobDataMap 定时任务的 元数据
* @param jobClass {@link org.quartz.Job} 定时任务的 真正执行逻辑定义类
*/
public JobDetail getJobDetail(JobKey jobKey, String description, JobDataMap jobDataMap, Class<? extends Job> jobClass) {
return JobBuilder.newJob(jobClass)
.withIdentity(jobKey)
.withDescription(description)
.setJobData(jobDataMap)
.usingJobData(jobDataMap)
.requestRecovery()
.storeDurably()
.build();
}
/**
* 获取Trigger (Job的触发器,执行规则)
*
* @param jobKey 定时任务的名称 组名
* @param description 定时任务的 描述
* @param jobDataMap 定时任务的 元数据
* @param cronExpression 定时任务的 执行cron表达式
*/
public Trigger getTrigger(JobKey jobKey, String description, JobDataMap jobDataMap, String cronExpression) {
return TriggerBuilder.newTrigger()
.withIdentity(jobKey.getName(), jobKey.getGroup())
.withDescription(description)
.withSchedule(CronScheduleBuilder.cronSchedule(cronExpression))
.usingJobData(jobDataMap)
.build();
}
public JobDataMap getJobDataMap(Map<?, ?> map) {
return map == null ? new JobDataMap() : new JobDataMap(map);
}
}