SpringBoot 2.2.5 整合Quartz,配置动态增删改查定时任务,并解决Job中不能注入Bean问题
前言:该博客主要是记录自己学习的过程,方便以后查看,当然也希望能够帮到大家。
由于工作上的需要,初步了解Quartz后进行使用。完整代码地址在结尾!!
第一步,在pom.xml文件中加入依赖,如下
<!-- Quartz -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
第二步,自定义JobFactory类
注意:我注入了一个 自定义的JobFactory ,然后把它设置为SchedulerFactoryBean的JobFactory。让其在具体job类实例化时使用spring的api来进行依赖注入,目的是因为我在具体的job中需要注入一些spring的bean。
import org.quartz.spi.TriggerFiredBundle;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.scheduling.quartz.AdaptableJobFactory;
import org.springframework.stereotype.Component;
/**
* @version 1.0
* @author jinhaoxun
* @date 2018-05-09
* @description Quartz创建JobFactory实例
*/
@Component
public class JobFactory extends AdaptableJobFactory {
/**
* AutowireCapableBeanFactory接口是BeanFactory的子类
* 可以连接和填充那些生命周期不被Spring管理的已存在的bean实例
*/
private AutowireCapableBeanFactory factory;
/**
* @author jinhaoxun
* @description 构造器
* @param factory
*/
public JobFactory(AutowireCapableBeanFactory factory) {
this.factory = factory;
}
/**
* @author jinhaoxun
* @description 创建Job实例
* @param bundle
* @return Object
*/
@Override
protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
Object job = super.createJobInstance(bundle);// 实例化对象
factory.autowireBean(job);// 进行注入(Spring管理该Bean)
return job;//返回对象
}
}
第三步,自定义QuartzConfig配置文件
import lombok.extern.slf4j.Slf4j;
import org.quartz.Scheduler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
/**
* @version 1.0
* @author jinhaoxun
* @date 2018-05-09
* @description Quartz配置
*/
@Slf4j
@Configuration
public class QuartzConfig {
private JobFactory jobFactory;
/**
* @author jinhaoxun
* @description 构造器
* @param jobFactory
*/
public QuartzConfig(JobFactory jobFactory){
this.jobFactory = jobFactory;
}
/**
* @author jinhaoxun
* @description 配置SchedulerFactoryBean,将一个方法产生为Bean并交给Spring容器管理
* @return SchedulerFactoryBean
*/
@Bean
public SchedulerFactoryBean schedulerFactoryBean() {
log.info("开始注入定时任务调度器工厂...");
SchedulerFactoryBean factory = new SchedulerFactoryBean();// Spring提供SchedulerFactoryBean为Scheduler提供配置信息,并被Spring容器管理其生命周期
factory.setJobFactory(jobFactory);// 设置自定义Job Factory,用于Spring管理Job bean
log.info("注入定时任务调度器工厂成功!");
return factory;
}
@Bean(name = "scheduler")
public Scheduler scheduler() {
return schedulerFactoryBean().getScheduler();
}
}
第四步,新增任务请求实体类AddSimpleJobReq,AddCronJobReq,DeleteJobReq,如下
AddSimpleJobReq
import lombok.Data;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* @Description: 新增Simple定时任务请求实体类
* @Author: jinhaoxun
* @Date: 2020/1/15 11:20
* @Version: 1.0.0
*/
@Data
public class AddSimpleJobReq {
private String jobClass;
private Date date;
private Map<String, String> params = new HashMap<>();
}
AddCronJobReq
import lombok.Data;
import java.util.HashMap;
import java.util.Map;
/**
* @Description: 新增Cron定时任务请求实体类
* @Author: jinhaoxun
* @Date: 2020/1/15 11:20
* @Version: 1.0.0
*/
@Data
public class AddCronJobReq {
private String jobName;
private String jobGroupName;
private String triggerName;
private String triggerGroupName;
private String jobClass;
private String date;
private Map<String, String> params = new HashMap<>();
}
DeleteJobReq
import lombok.Data;
/**
* @Description: 删除定时任务请求实体类
* @Author: jinhaoxun
* @Date: 2020/1/15 11:20
* @Version: 1.0.0
*/
@Data
public class DeleteJobReq {
private String jobName;
private String jobGroupName;
private String triggerName;
private String triggerGroupName;
}
第五步,自定义QuartzManager类,用于操作定时任务
import com.jinhaoxun.quartz.entity.request.AddCronJobReq;
import com.jinhaoxun.quartz.entity.request.AddSimpleJobReq;
import com.jinhaoxun.quartz.entity.request.DeleteJobReq;
import lombok.extern.slf4j.Slf4j;
import org.quartz.*;
import org.springframework.stereotype.Service;
import static org.quartz.DateBuilder.futureDate;
/**
* @Description: Quartz管理操作类
* @Author: jinhaoxun
* @Date: 2020/1/15 11:20
* @Version: 1.0.0
*/
@Slf4j
@Service
public class QuartzManager {
private Scheduler scheduler;
/**
* @author jinhaoxun
* @description 构造器
* @param scheduler 调度器
*/
public QuartzManager(Scheduler scheduler){
this.scheduler = scheduler;
}
/**
* quartz任务类包路径
*/
private static String jobUri = "com.jinhaoxun.quartz.job.";
/**
* @author jinhaoxun
* @description 添加一个Simple定时任务,只执行一次的定时任务
* @param addSimpleJobReq 参数对象
* @param taskId 任务ID,不能同名
* @throws RuntimeException
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public void addSimpleJob(AddSimpleJobReq addSimpleJobReq, String taskId) throws Exception {
String jobUrl = jobUri + addSimpleJobReq.getJobClass();
try {
Class<? extends Job> aClass = (Class<? extends Job>) Class.forName(jobUrl).newInstance().getClass();
// 任务名,任务组,任务执行类
JobDetail job = JobBuilder.newJob(aClass).withIdentity(taskId,
"JobGroup").build();
//增加任务ID参数
addSimpleJobReq.getParams().put("taskId",taskId);
// 添加任务参数
job.getJobDataMap().putAll(addSimpleJobReq.getParams());
// 转换为时间差,秒单位
int time = (int) (addSimpleJobReq.getDate().getTime() - System.currentTimeMillis()) / 1000;
SimpleTrigger trigger = (SimpleTrigger) TriggerBuilder.newTrigger()
.withIdentity(taskId, taskId + "TiggerGroup")
.startAt(futureDate(time, DateBuilder.IntervalUnit.SECOND))
.build();
// 调度容器设置JobDetail和Trigger
scheduler.scheduleJob(job, trigger);
if (!scheduler.isShutdown()) {
// 启动
scheduler.start();
}
} catch (Exception e) {
log.info("Quartz新增任务失败");
}
}
/**
* @author jinhaoxun
* @description 添加一个Cron定时任务,循环不断执行的定时任务
* @param addCronJobReq 参数对象
* @throws Exception
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public void addCronJob(AddCronJobReq addCronJobReq) throws Exception {
String jobUrl = jobUri + addCronJobReq.getJobClass();
try {
Class<? extends Job> aClass = (Class<? extends Job>) Class.forName(jobUrl).newInstance().getClass();
// 任务名,任务组,任务执行类
JobDetail job = JobBuilder.newJob(aClass).withIdentity(addCronJobReq.getJobName(),
addCronJobReq.getJobGroupName()).build();
// 添加任务参数
job.getJobDataMap().putAll(addCronJobReq.getParams());
// 创建触发器
CronTrigger trigger = (CronTrigger) TriggerBuilder.newTrigger()
// 触发器名,触发器组
.withIdentity(addCronJobReq.getTriggerName(), addCronJobReq.getTriggerGroupName())
// 触发器时间设定
.withSchedule(CronScheduleBuilder.cronSchedule(addCronJobReq.getDate()))
.build();
// 调度容器设置JobDetail和Trigger
scheduler.scheduleJob(job, trigger);
if (!scheduler.isShutdown()) {
// 启动
scheduler.start();
}
} catch (Exception e) {
log.info("Quartz新增任务失败");
}
}
/**
* @author jinhaoxun
* @description 修改一个任务的触发时间
* @param triggerName 触发器名
* @param triggerGroupName 触发器组名
* @param cron 时间设置,参考quartz说明文档
* @throws Exception
*/
public void modifyJobTime(String triggerName, String triggerGroupName, String cron) throws Exception {
try {
TriggerKey triggerKey = TriggerKey.triggerKey(triggerName, triggerGroupName);
CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
if (trigger == null) {
return;
}
String oldTime = trigger.getCronExpression();
if (!oldTime.equalsIgnoreCase(cron)) {
// 触发器
TriggerBuilder<Trigger> triggerBuilder = TriggerBuilder.newTrigger();
// 触发器名,触发器组
triggerBuilder.withIdentity(triggerName, triggerGroupName);
triggerBuilder.startNow();
// 触发器时间设定
triggerBuilder.withSchedule(CronScheduleBuilder.cronSchedule(cron));
// 创建Trigger对象
trigger = (CronTrigger) triggerBuilder.build();
// 方式一 :修改一个任务的触发时间
scheduler.rescheduleJob(triggerKey, trigger);
}
} catch (Exception e) {
log.info("Quartz修改任务失败");
}
}
/**
* @author jinhaoxun
* @description 移除一个任务
* @param deleteJobReq 参数对象
* @throws Exception
*/
public void removeJob(DeleteJobReq deleteJobReq) throws Exception {
try {
TriggerKey triggerKey = TriggerKey.triggerKey(deleteJobReq.getTriggerName(), deleteJobReq.getTriggerGroupName());
// 停止触发器
scheduler.pauseTrigger(triggerKey);
// 移除触发器
scheduler.unscheduleJob(triggerKey);
// 删除任务
scheduler.deleteJob(JobKey.jobKey(deleteJobReq.getJobName(), deleteJobReq.getJobGroupName()));
} catch (Exception e) {
log.info("Quartz删除改任务失败");
}
}
/**
* @author jinhaoxun
* @description 获取任务是否存在
* @param triggerName 触发器名
* @param triggerGroupName 触发器组名
* @return Boolean 返回操作结果
* 获取任务是否存在
* STATE_BLOCKED 4 阻塞
* STATE_COMPLETE 2 完成
* STATE_ERROR 3 错误
* STATE_NONE -1 不存在
* STATE_NORMAL 0 正常
* STATE_PAUSED 1 暂停
* @throws Exception
*/
public Boolean notExists(String triggerName, String triggerGroupName) throws Exception {
try {
if (scheduler.getTriggerState(TriggerKey.triggerKey(triggerName, triggerGroupName)) == Trigger.TriggerState.NORMAL){
return true;
}
} catch (Exception e) {
log.info("Quartz获取任务是否存在失败");
}
return false;
}
/**
* @author jinhaoxun
* @description 关闭调度器
* @throws RuntimeException
*/
public void shutdown() throws Exception {
try {
if(scheduler.isStarted()){
scheduler.shutdown(true);
}
} catch (Exception e) {
log.info("Quartz关闭调度器失败");
}
}
}
第六步,新增自定义任务类型,将要做的操作放到该类的execute方法中,主要该类的路径,要放到上面配置的com.jinhaoxun.quartz.job包里面
import lombok.extern.slf4j.Slf4j;
import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
/**
* @Description: Job测试类
* @Author: jinhaoxun
* @Date: 2020/1/15 11:20
* @Version: 1.0.0
*/
@Slf4j
public class JobTest implements Job {
/**
* @author jinhaoxun
* @description 重写任务内容
* @param jobExecutionContext 设置的key
* @throws
*/
@Override
public void execute(JobExecutionContext jobExecutionContext) {
//获取参数
JobDataMap dataMap = jobExecutionContext.getMergedJobDataMap();
String id = dataMap.getString("id");
String name = dataMap.getString("name");
try {
log.info("执行任务{},{}",id,name);
} catch (Exception e) {
log.info("Quartz执行失败");
}
}
}
第七步,编写单元测试类,QuartzApplicationTests,并进行测试,使用方法基本都有注释
import com.luoyu.quartz.entity.request.AddCronJobReq;
import com.luoyu.quartz.entity.request.AddSimpleJobReq;
import com.luoyu.quartz.entity.request.DeleteJobReq;
import com.luoyu.quartz.manager.QuartzManager;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
@Slf4j
// 获取启动类,加载配置,确定装载 Spring 程序的装载方法,它回去寻找 主配置启动类(被 @SpringBootApplication 注解的)
@SpringBootTest
class QuartzApplicationTests {
@Autowired
private QuartzManager quartzManager;
@Test
void addSimpleJobTest() throws Exception {
Map<String, String> params = new HashMap<>();
params.put("id","测试id");
params.put("name","测试name");
Calendar beforeTime = Calendar.getInstance();
// 5 秒之后的时间
beforeTime.add(Calendar.SECOND, 5);
Date beforeDate = beforeTime.getTime();
AddSimpleJobReq addSimpleJobReq = new AddSimpleJobReq();
addSimpleJobReq.setDate(beforeDate);
addSimpleJobReq.setJobClass("JobTest");
addSimpleJobReq.setParams(params);
quartzManager.addSimpleJob(addSimpleJobReq, "123");
// 让主线程睡眠60秒
Thread.currentThread().sleep(60000);
}
@Test
void addCronJobTest() throws Exception {
Map<String, String> params = new HashMap<>();
params.put("id","测试id");
params.put("name","测试name");
AddCronJobReq addCronJobReq = new AddCronJobReq();
//每 5 秒执行一次
addCronJobReq.setDate("0/5 * * * * ?");
addCronJobReq.setJobClass("JobTest");
addCronJobReq.setJobGroupName("JobGroupName");
addCronJobReq.setJobName("JobName");
addCronJobReq.setParams(params);
addCronJobReq.setTriggerGroupName("triggerGroupName");
addCronJobReq.setTriggerName("triggerName");
quartzManager.addCronJob(addCronJobReq);
// 让主线程睡眠60秒
Thread.currentThread().sleep(60000);
}
@Test
void removeJobTest() throws Exception {
Map<String, String> params = new HashMap<>();
params.put("id","测试id");
params.put("name","测试name");
Calendar beforeTime = Calendar.getInstance();
// 5 秒之后的时间
beforeTime.add(Calendar.SECOND, 5);
Date beforeDate = beforeTime.getTime();
AddSimpleJobReq addSimpleJobReq = new AddSimpleJobReq();
addSimpleJobReq.setDate(beforeDate);
addSimpleJobReq.setJobClass("JobTest");
addSimpleJobReq.setParams(params);
quartzManager.addSimpleJob(addSimpleJobReq, "123");
DeleteJobReq deleteJobReq = new DeleteJobReq();
deleteJobReq.setJobName("123");
deleteJobReq.setJobGroupName("123JobGroup");
deleteJobReq.setTriggerName("123");
deleteJobReq.setTriggerGroupName("123TiggerGroup");
quartzManager.removeJob(deleteJobReq);
// 让主线程睡眠60秒
Thread.currentThread().sleep(60000);
}
@Test
void shutdownTest() throws Exception {
quartzManager.shutdown();
}
@BeforeEach
void testBefore(){
log.info("测试开始!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
}
@AfterEach
void testAfter(){
log.info("测试结束!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
}
}
完整代码地址:https://github.com/Jinhx128/springboot-demo
注:此工程包含多个module,本文所用代码均在quartz-demo模块下
后记:本次分享到此结束,本人水平有限,难免有错误或遗漏之处,望大家指正和谅解,欢迎评论留言。
赞 (0)