Springboot整个Quartz实现动态定时任务的示例代码
作者:小揪揪 发布时间:2023-04-12 20:30:02
简介
Quartz是一款功能强大的任务调度器,可以实现较为复杂的调度功能,如每月一号执行、每天凌晨执行、每周五执行等等,还支持分布式调度。本文使用Springboot+Mybatis+Quartz实现对定时任务的增、删、改、查、启用、停用等功能。并把定时任务持久化到数据库以及支持集群。
Quartz的3个基本要素
Scheduler:调度器。所有的调度都是由它控制。
Trigger: 触发器。决定什么时候来执行任务。
JobDetail & Job: JobDetail定义的是任务数据,而真正的执行逻辑是在Job中。使用JobDetail + Job而不是Job,这是因为任务是有可能并发执行,如果Scheduler直接使用Job,就会存在对同一个Job实例并发访问的问题。而JobDetail & Job 方式,sheduler每次执行,都会根据JobDetail创建一个新的Job实例,这样就可以规避并发访问的问题。
如何使用Quartz
1.添加依赖
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.2.3</version>
</dependency>
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz-jobs</artifactId>
<version>2.2.3</version>
</dependency>
2.创建配置文件
在maven项目的resource目录下创建quartz.properties
org.quartz.scheduler.instanceName = MyScheduler
org.quartz.scheduler.instanceId = AUTO
org.quartz.scheduler.rmi.export = false
org.quartz.scheduler.rmi.proxy = false
org.quartz.scheduler.wrapJobExecutionInUserTransaction = false
#线程池配置
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount = 10
org.quartz.threadPool.threadPriority = 5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true
#持久化配置
org.quartz.jobStore.misfireThreshold = 50000
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
#支持集群
org.quartz.jobStore.isClustered = true
org.quartz.jobStore.useProperties:true
org.quartz.jobStore.clusterCheckinInterval = 15000
#使用weblogic连接Oracle驱动
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.oracle.weblogic.WebLogicOracleDelegate
#org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.jobStore.tablePrefix = QRTZ_
org.quartz.jobStore.dataSource = qzDS
#数据源连接信息,quartz默认使用c3p0数据源可以被自定义数据源覆盖
org.quartz.dataSource.qzDS.driver = oracle.jdbc.driver.OracleDriver
org.quartz.dataSource.qzDS.URL = jdbc:oracle:thin:@localhost:1521/XE
org.quartz.dataSource.qzDS.user = root
org.quartz.dataSource.qzDS.password = 123456
org.quartz.dataSource.qzDS.maxConnections = 10
说明:在使用quartz做持久化的时候需要用到quartz的11张表,可以去quartz官网下载对应版本的quartz,解压打开docs/dbTables里面有对应数据库的建表语句。关于quartz.properties配置的详细解释可以查看quartz官网。另外新建一张表TB_APP_QUARTZ用于存放定时任务基本信息和描述等信息,定时任务的增、删、改、执行等功能与此表没有任何关系。
quartz的11张表:
//TB_APP_QUARTZ表的实体类
public class AppQuartz {
private Integer quartzId; //id 主键
private String jobName; //任务名称
private String jobGroup; //任务分组
private String startTime; //任务开始时间
private String cronExpression; //corn表达式
private String invokeParam;//需要传递的参数
...省略set get
}
3.Quartz配置
/**
* 创建job 实例工厂,解决spring注入问题,如果使用默认会导致spring的@Autowired 无法注入问题
* @author LLQ
*
*/
@Component
public class JobFactory extends AdaptableJobFactory{
@Autowired
private AutowireCapableBeanFactory capableBeanFactory;
@Override
protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
//调用父类的方法
Object jobInstance = super.createJobInstance(bundle);
//进行注入
capableBeanFactory.autowireBean(jobInstance);
return jobInstance;
}
}
@Configuration
public class SchedulerConfig implements ApplicationListener<ContextRefreshedEvent>{
@Autowired
private JobFactory jobFactory;
@Autowired
@Qualifier("dataSource")
private DataSource primaryDataSource;
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
System.out.println("任务已经启动..."+event.getSource());
}
@Bean
public SchedulerFactoryBean schedulerFactoryBean() throws IOException {
//获取配置属性
PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
propertiesFactoryBean.setLocation(new ClassPathResource("/quartz.properties"));
//在quartz.properties中的属性被读取并注入后再初始化对象
propertiesFactoryBean.afterPropertiesSet();
//创建SchedulerFactoryBean
SchedulerFactoryBean factory = new SchedulerFactoryBean();
factory.setQuartzProperties(propertiesFactoryBean.getObject());
//使用数据源,自定义数据源
factory.setDataSource(this.primaryDataSource);
factory.setJobFactory(jobFactory);
factory.setWaitForJobsToCompleteOnShutdown(true);//这样当spring关闭时,会等待所有已经启动的quartz job结束后spring才能完全shutdown。
factory.setOverwriteExistingJobs(false);
factory.setStartupDelay(1);
return factory;
}
/*
* 通过SchedulerFactoryBean获取Scheduler的实例
*/
@Bean(name="scheduler")
public Scheduler scheduler() throws IOException {
return schedulerFactoryBean().getScheduler();
}
@Bean
public QuartzInitializerListener executorListener() {
return new QuartzInitializerListener();
}
}
4.创建定时任务服务
@Service
public class JobUtil {
@Autowired
@Qualifier("scheduler")
private Scheduler scheduler;
/**
* 新建一个任务
*
*/
public String addJob(AppQuartz appQuartz) throws Exception {
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date=df.parse(appQuartz.getStartTime());
if (!CronExpression.isValidExpression(appQuartz.getCronExpression())) {
return "Illegal cron expression"; //表达式格式不正确
}
JobDetail jobDetail=null;
//构建job信息
if("JobOne".equals(appQuartz.getJobGroup())) {
jobDetail = JobBuilder.newJob(JobOne.class).withIdentity(appQuartz.getJobName(), appQuartz.getJobGroup()).build();
}
if("JobTwo".equals(appQuartz.getJobGroup())) {
jobDetail = JobBuilder.newJob(JobTwo.class).withIdentity(appQuartz.getJobName(), appQuartz.getJobGroup()).build();
}
//表达式调度构建器(即任务执行的时间,不立即执行)
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(appQuartz.getCronExpression()).withMisfireHandlingInstructionDoNothing();
//按新的cronExpression表达式构建一个新的trigger
CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(appQuartz.getJobName(), appQuartz.getJobGroup()).startAt(date)
.withSchedule(scheduleBuilder).build();
//传递参数
if(appQuartz.getInvokeParam()!=null && !"".equals(appQuartz.getInvokeParam())) {
trigger.getJobDataMap().put("invokeParam",appQuartz.getInvokeParam());
}
scheduler.scheduleJob(jobDetail, trigger);
// pauseJob(appQuartz.getJobName(),appQuartz.getJobGroup());
return "success";
}
/**
* 获取Job状态
* @param jobName
* @param jobGroup
* @return
* @throws SchedulerException
*/
public String getJobState(String jobName, String jobGroup) throws SchedulerException {
TriggerKey triggerKey = new TriggerKey(jobName, jobGroup);
return scheduler.getTriggerState(triggerKey).name();
}
//暂停所有任务
public void pauseAllJob() throws SchedulerException {
scheduler.pauseAll();
}
//暂停任务
public String pauseJob(String jobName, String jobGroup) throws SchedulerException {
JobKey jobKey = new JobKey(jobName, jobGroup);
JobDetail jobDetail = scheduler.getJobDetail(jobKey);
if (jobDetail == null) {
return "fail";
}else {
scheduler.pauseJob(jobKey);
return "success";
}
}
//恢复所有任务
public void resumeAllJob() throws SchedulerException {
scheduler.resumeAll();
}
// 恢复某个任务
public String resumeJob(String jobName, String jobGroup) throws SchedulerException {
JobKey jobKey = new JobKey(jobName, jobGroup);
JobDetail jobDetail = scheduler.getJobDetail(jobKey);
if (jobDetail == null) {
return "fail";
}else {
scheduler.resumeJob(jobKey);
return "success";
}
}
//删除某个任务
public String deleteJob(AppQuartz appQuartz) throws SchedulerException {
JobKey jobKey = new JobKey(appQuartz.getJobName(), appQuartz.getJobGroup());
JobDetail jobDetail = scheduler.getJobDetail(jobKey);
if (jobDetail == null ) {
return "jobDetail is null";
}else if(!scheduler.checkExists(jobKey)) {
return "jobKey is not exists";
}else {
scheduler.deleteJob(jobKey);
return "success";
}
}
//修改任务
public String modifyJob(AppQuartz appQuartz) throws SchedulerException {
if (!CronExpression.isValidExpression(appQuartz.getCronExpression())) {
return "Illegal cron expression";
}
TriggerKey triggerKey = TriggerKey.triggerKey(appQuartz.getJobName(),appQuartz.getJobGroup());
JobKey jobKey = new JobKey(appQuartz.getJobName(),appQuartz.getJobGroup());
if (scheduler.checkExists(jobKey) && scheduler.checkExists(triggerKey)) {
CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
//表达式调度构建器,不立即执行
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(appQuartz.getCronExpression()).withMisfireHandlingInstructionDoNothing();
//按新的cronExpression表达式重新构建trigger
trigger = trigger.getTriggerBuilder().withIdentity(triggerKey)
.withSchedule(scheduleBuilder).build();
//修改参数
if(!trigger.getJobDataMap().get("invokeParam").equals(appQuartz.getInvokeParam())) {
trigger.getJobDataMap().put("invokeParam",appQuartz.getInvokeParam());
}
//按新的trigger重新设置job执行
scheduler.rescheduleJob(triggerKey, trigger);
return "success";
}else {
return "job or trigger not exists";
}
}
}
@PersistJobDataAfterExecution
@DisallowConcurrentExecution
@Component
public class JonOne implements Job{
@Override
public void execute(JobExecutionContext context) throws JobExecutionException{
JobDataMap data=context.getTrigger().getJobDataMap();
String invokeParam =(String) data.get("invokeParam");
//在这里实现业务逻辑
}
}
@PersistJobDataAfterExecution
@DisallowConcurrentExecution
@Component
public class JobTwo implements Job{
@Override
public void execute(JobExecutionContext context) throws JobExecutionException{
JobDataMap data=context.getTrigger().getJobDataMap();
String invokeParam =(String) data.get("invokeParam");
//在这里实现业务逻辑
}
}
说明:每个定时任务都必须有一个分组,名称和corn表达式,corn表达式也就是定时任务的触发时间,关于corn表达式格式以及含义可以参考一些网络资源。每个定时任务都有一个入口类在这里我把类名当成定时任务的分组名称,例如:只要创建定时任务的分组是JobOne的都会执行JobOne这个任务类里面的逻辑。如果定时任务需要额外的参数可以使用JobDataMap传递参数,当然也可以从数据库中获取需要的数据。@PersistJobDataAfterExecution和@DisallowConcurrentExecution注解是不让某个定时任务并发执行,只有等当前任务完成下一个任务才会去执行。
5.封装定时任务接口
@RestController
public class JobController {
@Autowired
private JobUtil jobUtil;
@Autowired
private AppQuartzService appQuartzService;
//添加一个job
@RequestMapping(value="/addJob",method=RequestMethod.POST)
public ReturnMsg addjob(@RequestBody AppQuartz appQuartz) throws Exception {
appQuartzService.insertAppQuartzSer(appQuartz);
result=jobUtil.addJob(appQuartz);
}
//暂停job
@RequestMapping(value="/pauseJob",method=RequestMethod.POST)
public ReturnMsg pausejob(@RequestBody Integer[]quartzIds) throws Exception {
AppQuartz appQuartz=null;
if(quartzIds.length>0){
for(Integer quartzId:quartzIds) {
appQuartz=appQuartzService.selectAppQuartzByIdSer(quartzId).get(0);
jobUtil.pauseJob(appQuartz.getJobName(), appQuartz.getJobGroup());
}
return new ReturnMsg("200","success pauseJob");
}else {
return new ReturnMsg("404","fail pauseJob");
}
}
//恢复job
@RequestMapping(value="/resumeJob",method=RequestMethod.POST)
public ReturnMsg resumejob(@RequestBody Integer[]quartzIds) throws Exception {
AppQuartz appQuartz=null;
if(quartzIds.length>0) {
for(Integer quartzId:quartzIds) {
appQuartz=appQuartzService.selectAppQuartzByIdSer(quartzId).get(0);
jobUtil.resumeJob(appQuartz.getJobName(), appQuartz.getJobGroup());
}
return new ReturnMsg("200","success resumeJob");
}else {
return new ReturnMsg("404","fail resumeJob");
}
}
//删除job
@RequestMapping(value="/deletJob",method=RequestMethod.POST)
public ReturnMsg deletjob(@RequestBody Integer[]quartzIds) throws Exception {
AppQuartz appQuartz=null;
for(Integer quartzId:quartzIds) {
appQuartz=appQuartzService.selectAppQuartzByIdSer(quartzId).get(0);
String ret=jobUtil.deleteJob(appQuartz);
if("success".equals(ret)) {
appQuartzService.deleteAppQuartzByIdSer(quartzId);
}
}
return new ReturnMsg("200","success deleteJob");
}
//修改
@RequestMapping(value="/updateJob",method=RequestMethod.POST)
public ReturnMsg modifyJob(@RequestBody AppQuartz appQuartz) throws Exception {
String ret= jobUtil.modifyJob(appQuartz);
if("success".equals(ret)) {
appQuartzService.updateAppQuartzSer(appQuartz);
return new ReturnMsg("200","success updateJob",ret);
}else {
return new ReturnMsg("404","fail updateJob",ret);
}
}
//暂停所有
@RequestMapping(value="/pauseAll",method=RequestMethod.GET)
public ReturnMsg pauseAllJob() throws Exception {
jobUtil.pauseAllJob();
return new ReturnMsg("200","success pauseAll");
}
//恢复所有
@RequestMapping(value="/repauseAll",method=RequestMethod.GET)
public ReturnMsg repauseAllJob() throws Exception {
jobUtil.resumeAllJob();
return new ReturnMsg("200","success repauseAll");
}
}
来源:https://segmentfault.com/a/1190000016554033
猜你喜欢
- easyExcel简介:Java领域解析、生成Excel比较有名的框架有Apache poi、jxl等。但他们都存在一个严重的问题就是非常的
- 目录断言对象、数组、集合ObjectUtilsStringUtilsCollectionUtils文件、资源、IO 流FileCopyUti
- 一、前言前面我们学习了多态中的转型,那么现在我们开始学习抽象类的概述和使用二、抽象类生活大多事物是具有抽象含义的,比如我说一个生物,你想不到
- Java与C++实现相同的MD5加密算法1、Java版package com.lyz.utils.common;import java.io
- 概要: ShardingSphere是一套开源的分布式数据库中间件解决方案组成的生态圈,它由Sharding-JDBC、Sharding-P
- Java8Stream流操作List去重根据属性去重整体去重使用distinctArrayList<LabelInfoDTO>
- 目录引言配置yml文件创建数据源配置类为每个数据库创建配置类引言今天为大家带来一些非常有用的实战技巧,比如在我们需要对两个数据库进行操作的时
- 一、什么是组合模式定义:将对象以树形结构组织起来,以达成“部分-整体”的层次结构,使得客户端对单个对象和组合对象的使用具有一致性。动机(Mo
- 错误详情:java.lang.NoSuchMethodException: [Lorg.springframework.web.multip
- 本文实例为大家分享了Java界面编程实现界面跳转的具体代码,供大家参考,具体内容如下在事件处理中创建对象public void action
- springcloud微服务包含的技术种类众多,eureka作为其注册中心,一直处于主流,但在今年已经处于永久停更状态,但其优秀的能力还是值
- 前言此文适合了解了es相关概念以及基础知识的同学阅读elasticsearch简介Elasticsearch是一个基于Lucene的搜索服务
- 一、基本介绍 1、介绍学习很多算法知识,力争做到最优解的学习过程中,很多时候都会遇到PriorityQueue(优先队列)。一个基
- 冒泡排序:就是按索引逐次比较相邻的两个元素,如果大于/小于(取决于需要升序排还是降序排),则置换,否则不做改变这样一轮下来,比较了n-1次,
- 本文实例讲述了Java实现的Base64加密算法。分享给大家供大家参考,具体如下:一 算法实现1、JDK2、Commonc Codec3、B
- 引言之前关于事务的文章已介绍了事务的概念以及事务的四个属性(ACID),相信你对事务应该有所认识和了解。本篇文章是关于事务的隔离性,介绍数据
- 注解的介绍@ControllerAdvice@ControllerAdvice注解是Spring3.2中新增的注解,学名是Controlle
- 单元测试是程序员对代码的自测,一般公司都会严格要求单元测试,这是对自己代码的负责,也是对代码的敬畏。一般单元测试都是测试Service层,下
- 以前使用spring的使用要注入property要配置PropertyPlaceholder的bean对象。在springboot除&nbs
- 1、代码设计的代理模式代理模式属于构建型模式(Proxy),提供了对目标对象的一种访问方式; 即通过代理对象访问目标对象。这样做的好处是:可