Quartz实现JAVA定时任务的动态配置的方法

先说点无关本文的问题,这段时间特别的不爽,可能有些同学也遇到过。其实也可以说是小事一桩,但感觉也是不容忽视的。我刚毕业时的公司,每个人每次提交代码都有着严格的规范,像table和space的缩进都有严格的要求,可以说你不遵守开发规范就相当于线上bug问题,还是比较严重的。现在发现外面的公司真的是没那么重视这个不重要却又特别重要的问题啊,啊啊啊啊啊啊!!!

什么是动态配置定时任务?

回归正题,说下这次主题,动态配置。没接触过定时任务的同学可以先看下此篇:JAVA定时任务实现的几种方式

定时任务实现方式千人千种,不过基础的无外乎 1、JDK 的Timer类 2、Quartz 3、SpringTask  。生产上三种方式我都有使用过。但是使用过程中用的最多的便是xml配置的方式,这种方式最简单,无代码侵入,也比较好理解。

但是却有个致命的缺点,比如你要改某个任务的触发时间,亦或是你要新增一个任务,暂停一个任务。怎么做?

停应用!改XML配置!重新启动!

是不是很致命。最近重新研究了下Quartz的配置,实现了不停机添加、暂停、删除、立即触发任务的方法,在本篇分享出来,其实也不算是完全的研究,在上家公司已经有大佬实现了,这次是也是基于大佬的理解重新实现下。

国际惯例~先看效果图   ps:文末有彩蛋哦~~

管理界面:

效果图:实在不知道该跑什么了,每隔十秒打一段话吧

技术实现 maven依赖

使用springboot做框架支持

<!-- quartz -->
<dependency>
 <groupId>org.quartz-scheduler</groupId>
 <artifactId>quartz</artifactId>
 <version>2.2.1</version>
</dependency>
<dependency>
 <groupId>org.springframework</groupId>
 <artifactId>spring-context-support</artifactId>
</dependency>

数据表

CREATE TABLE `sys_task` (
 `id` bigint(20) NOT NULL AUTO_INCREMENT,
 `job_name` varchar(255) DEFAULT NULL COMMENT '任务名',
 `description` varchar(255) DEFAULT NULL COMMENT '任务描述',
 `cron_expression` varchar(255) DEFAULT NULL COMMENT 'cron表达式',
 `bean_class` varchar(255) DEFAULT NULL COMMENT '任务执行时调用哪个类的方法 包名+类名',
 `job_status` varchar(255) DEFAULT NULL COMMENT '任务状态',
 `job_group` varchar(255) DEFAULT NULL COMMENT '任务分组',
 `create_user` varchar(64) DEFAULT NULL COMMENT '创建者',
 `create_time` datetime DEFAULT NULL COMMENT '创建时间',
 `update_user` varchar(64) DEFAULT NULL COMMENT '更新者',
 `update_time` datetime DEFAULT NULL COMMENT '更新时间',
 PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=32 DEFAULT CHARSET=utf8;

实现步骤

①启动项目,启动task监听

②读取数据库,将开启的任务job和trigger加载到scheduler调度器

③根据任务调度运行job类

④每次运行利用AdaptableJobFactory实例化job类,以便注入要运行的service

听着是不是很简单,但却还是一头雾水,且听我慢慢道来~~

代码逻辑

第一步:启动项目,加载监听

Quartz配置

Springboot的配置方法,常规Spring项目可以在xml中配置

@Configuration
public class QuartzConfigration {

 @Autowired
 private JobFactory jobFactory;

 @Bean
 public SchedulerFactoryBean schedulerFactoryBean() {
  SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
  try {
   schedulerFactoryBean.setOverwriteExistingJobs(true);
   schedulerFactoryBean.setQuartzProperties(quartzProperties());
   schedulerFactoryBean.setJobFactory(jobFactory);
  } catch (Exception e) {
   e.printStackTrace();
  }
  return schedulerFactoryBean;
 }

 // 指定quartz.properties,可在配置文件中配置相关属性
 @Bean
 public Properties quartzProperties() throws IOException {
  PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
  propertiesFactoryBean.setLocation(new ClassPathResource("/config/quartz.properties"));
  propertiesFactoryBean.afterPropertiesSet();
  return propertiesFactoryBean.getObject();
 }

 // 创建schedule
 @Bean(name = "scheduler")
 public Scheduler scheduler() {
  return schedulerFactoryBean().getScheduler();
 }
}

监听器

@Component
@Order(value = 1)
public class ScheduleJobInitListener implements CommandLineRunner {

 @Autowired
 TaskService scheduleJobService;

 @Override
 public void run(String... arg0) throws Exception {
  try {
   scheduleJobService.initSchedule();
  } catch (Exception e) {
   e.printStackTrace();
  }

 }

}

CommandLineRunner类似Spring框架的ApplicationListener监听器。

官方的解释是:

Interface used to indicate that a bean should run when it is contained within a SpringApplication. Multiple CommandLineRunner beans can be defined within the same application context and can be ordered using the Ordered interface or Order @Order annotation.

第二步:读取数据库,加载scheduler调度器

job方法

@Override
 public void initSchedule() throws SchedulerException {
  // 这里获取任务信息数据
  List<TaskDO> jobList = taskMapper.list();
  for (TaskDO task : jobList) {
   if (JobStatusEnum.RUNNING.getCode().equals(task.getJobStatus())) {
    quartzManager.addJob(task);
   }
  }
 }

添加任务到Quartz调度器

/**
  * 添加任务*/
 @SuppressWarnings("unchecked")
 public void addJob(TaskDO task) {
  try {
   // 创建jobDetail实例,绑定Job实现类
   // 指明job的名称,所在组的名称,以及绑定job类
   Class<? extends Job> jobClass = (Class<? extends Job>) (Class.forName(task.getBeanClass()).newInstance()
     .getClass());
   JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(task.getJobName(), task.getJobGroup())// 任务名称和组构成任务key
     .build();
   // 定义调度触发规则
   // 使用cornTrigger规则
   Trigger trigger = TriggerBuilder.newTrigger().withIdentity(task.getJobName(), task.getJobGroup())// 触发器key
     .startAt(DateBuilder.futureDate(1, IntervalUnit.SECOND))
     .withSchedule(CronScheduleBuilder.cronSchedule(task.getCronExpression())).startNow().build();
   // 把作业和触发器注册到任务调度中
   scheduler.scheduleJob(jobDetail, trigger);
   // 启动
   if (!scheduler.isShutdown()) {
    scheduler.start();
   }
  } catch (Exception e) {
   e.printStackTrace();
  }
 }

Scheduler作为Quartz的核心调度器,有将近50多个API接口,包括任务的添加,暂停,恢复,删除等一系列的API,这里仅介绍一些常用的,想要了解更多可以稍后看下彩蛋部分。

1、start()方法:只有调用start()方法后,Scheduler线程才开始启动触发器trigger,运行job

2、pauseJob(JobKey jobKey) :根据指定的JobDetail key暂停job。

3、resumeJob(JobKey jobKey) :根据指定的key恢复一个job。

4、deleteJob(JobKey jobKey) :删除一个job

5、triggerJob(JobKey jobKey) :触发一个JobDetail(现在执行)。

6、rescheduleJob(TriggerKey triggerKey, Trigger newTrigger):

用给定的键删除触发器,并存储新的触发器,它必须与同一个作业相关联(新触发器必须具有指定的作业名和组)-然而,新触发器不必具有与旧触发器相同的名称。

第三步:根据任务调度运行job类

其实这一步是不需要我们编写的,在我们将正确的JobDetail 和 Trigger 表达式加载到任务调度后,调度器会自动触发任务的执行

第四步:实例化job类,注入要运行的service

工厂类

@Component
public class JobFactory extends AdaptableJobFactory {
 //这个对象Spring会帮我们自动注入进来,也属于Spring技术范畴.
 //为什么需要这个类呢,在我写的这个demo中,大家可以将此类删掉,发现程序也可以正确运行,可是我为什么还是加上呢。
 //大家可以看下我们的任务类,大家可以看到Job对象的实例化过程是在Quartz中进行的,这时候我们将spring的东西注入进来,肯定是行不通的,所以需要这个类
 @Autowired
 private AutowireCapableBeanFactory capableBeanFactory;

 @Override
 protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
  //调用父类的方法
  Object jobInstance = super.createJobInstance(bundle);
  //进行注入
  capableBeanFactory.autowireBean(jobInstance);
  return jobInstance;
 }
}

任务类

@DisallowConcurrentExecution //作业不并发
@Component
public class HelloWorldJob implements Job{

 @Override
 public void execute(JobExecutionContext arg0) throws JobExecutionException {

  System.out.println("欢迎使用yyblog,这是一个定时任务 --小卖铺的老爷爷!"+ DateUtils.fullTime(new Date()));

 }

}

好了,大功告成,一个简单的动态配置的定时任务已经完成。是不是so easy,下面我们再来简单实现下其他的几种常用的api吧。

暂停一个job

public void pauseJob(TaskDO task) throws SchedulerException {
  JobKey jobKey = JobKey.jobKey(task.getJobName(), task.getJobGroup());
  scheduler.pauseJob(jobKey);
 }

恢复一个job

 public void resumeJob(TaskDO task) throws SchedulerException {
  JobKey jobKey = JobKey.jobKey(task.getJobName(), task.getJobGroup());
  scheduler.resumeJob(jobKey);
 }

删除一个job

 public void deleteJob(TaskDO task) throws SchedulerException {
  JobKey jobKey = JobKey.jobKey(task.getJobName(), task.getJobGroup());
  scheduler.deleteJob(jobKey);
 }

立即触发job

public void runJobNow(TaskDO task) throws SchedulerException {
  JobKey jobKey = JobKey.jobKey(task.getJobName(), task.getJobGroup());
  scheduler.triggerJob(jobKey);
 }

更新job表达式

public void updateJobCron(TaskDO task) throws SchedulerException {

  TriggerKey triggerKey = TriggerKey.triggerKey(task.getJobName(), task.getJobGroup());

  CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);

  CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(task.getCronExpression());

  trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();

  scheduler.rescheduleJob(triggerKey, trigger);
 }

彩蛋部分~

好了,正文部分基本上就这些,不多废话了,本文没有太多的讲解原理,只是简单的应用,水平不够还望大家见谅。

看了上面的效果图是不是还是有种看的见摸不着的感觉,老爷爷这里也做了个线上的实例供大家体验~有兴趣的同学可以亲自试验一下。

实例地址: http://www.laoyeye.net/management/index 账号/密码:test/123456  菜单:系统设置/计划任务

当然了还有最重要的源码和API文档我也不会忘的。

源码地址: https://github.com/allanzhuo/yyblog

Quartz文档地址: https://github.com/allanzhuo/yyblog/tree/master/doc

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • spring定时任务执行两次及tomcat部署缓慢问题的解决方法

    一.spring定时任务执行两次 问题重现和解析 最近使用quartz定时任务框架,结果发现开发环境执行无任何问题,部署到服务器上后,发现同一时间任务执行了多次.经过搜索发现是服务器上tomcat的配置文件出现了问题. 原来的配置文件--server.xml如下: <Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true">

  • Java实现终止线程池中正在运行的定时任务

    最近项目中遇到了一个新的需求,就是实现一个可以动态添加定时任务的功能.说到这里,有人可能会说简单啊,使用quartz就好了,简单粗暴.然而quartz框架太重了,小项目根本不好操作啊.当然,也有人会说,jdk提供了timer的接口啊,完全够用啊.但是我们项目的需求完全是多线程的模型啊,而timer是单线程的,so,楼主最后还是选择了jdk的线程池. 线程池是什么 Java通过Executors提供四种线程池,分别为: newCachedThreadPool :创建一个可缓存线程池,如果线程池长度

  • Spring Boot与Kotlin定时任务的示例(Scheduling Tasks)

    在编写Spring Boot应用中会遇到这样的场景,比如:需要定时地发送一些短信.邮件之类的操作,也可能会定时地检查和监控一些标志.参数等. 创建定时任务 在Spring Boot中编写定时任务是非常简单的事,下面通过实例介绍如何在Spring Boot中创建定时任务,实现每过5秒输出一下当前时间. 在Spring Boot的主类中加入@EnableScheduling注解,启用定时任务的配置 import org.springframework.boot.SpringApplication i

  • springBoot定时任务处理类的实现代码

    首先在启动类上添加注解:@EnableScheduling 来开启定时任务 @SpringBootApplication @EnableScheduling public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } 然后新建定时任务类 @Component public class QuartzService { /

  • springboot集成schedule实现定时任务

    背景 在项目开发过程中,我们经常需要执行具有周期性的任务.通过定时任务可以很好的帮助我们实现. 我们拿常用的几种定时任务框架做一个比较: 从以上表格可以看出,Spring Schedule框架功能完善,简单易用.对于中小型项目需求,Spring Schedule是完全可以胜任的. 1.springboot集成schedule 1.1 添加maven依赖包 由于Spring Schedule包含在spring-boot-starter基础模块中了,所有不需要增加额外的依赖. <dependenci

  • 最流行的java后台框架spring quartz定时任务

    配置quartz 在spring中需要三个jar包: quartz-1.8.5.jar.commons-collections-3.2.1.jar.commons-logging-1.1.jar 首先要配置我们的spring.xml xmlns 多加下面的内容. xmlns:task="http://www.springframework.org/schema/task" 然后xsi:schemaLocation多加下面的内容. http://www.springframework.o

  • 在Java Web项目中添加定时任务的方法

    在Java Web程序中加入定时任务,这里介绍两种方式:1.使用监听器注入:2.使用Spring注解@Scheduled注入. 推荐使用第二种形式. 一.使用监听器注入 ①:创建监听器类: import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; public class TimerDataTaskListener implements ServletContextListener

  • Quartz实现JAVA定时任务的动态配置的方法

    先说点无关本文的问题,这段时间特别的不爽,可能有些同学也遇到过.其实也可以说是小事一桩,但感觉也是不容忽视的.我刚毕业时的公司,每个人每次提交代码都有着严格的规范,像table和space的缩进都有严格的要求,可以说你不遵守开发规范就相当于线上bug问题,还是比较严重的.现在发现外面的公司真的是没那么重视这个不重要却又特别重要的问题啊,啊啊啊啊啊啊!!! 什么是动态配置定时任务? 回归正题,说下这次主题,动态配置.没接触过定时任务的同学可以先看下此篇:JAVA定时任务实现的几种方式 定时任务实现

  • Springboot自带定时任务实现动态配置Cron参数方式

    目录 Springboot自带定时任务实现动态配置Cron参数 SpringBoot定时任务的四种实现方式(主要) spring动态配置cron表达式,不需要停服 SchedulingConfigurer接口实现动态加载cron表达式 Springboot自带定时任务实现动态配置Cron参数 同学们,我今天分享一下SpringBoot动态配置Cron参数.场景是这样子的:后台管理界面对定时任务进行管理,可动态修改执行时间,然后保存入库,每次任务执行前从库里查询时间,以达到动态修改Cron参数的效

  • 通过java反射机制动态调用某方法的总结(推荐)

    如下: public Object invokeMethod(String className, String methodName, Object[] args) throws Exception{ Class ownerClass = Class.forName(className); Object owner = ownerClass.newInstance(); Class[] argsClass = new Class[args.length]; for (int i = 0, j =

  • 详解SpringBoot配置文件启动时动态配置参数方法

    序言 当我们要同时启用多个项目而又要使用不同端口或者变换配置属性时,我们可以在配置文件中设置${变量名}的变量来获取启动时传入的参数,从而实现了动态配置参数,使启用项目更加灵活 例子 server: port: ${PORT:50101} #服务端口 spring: application: name: xc‐govern‐center #指定服务名 eureka: client: registerWithEureka: true #服务注册,是否将自己注册到Eureka服务中 fetchReg

  • springboot多环境进行动态配置的方法

    目录 一.如何配置多环境 二.生效多环境的多种方式 2.1.spring.config.name 2.2.spring.profiles.active 2.3.pom中<profiles>标签 三.总结 在平时的开发中,经常会有多个环境,如何管理多个环境中的配置呐?一个是我们本地的开发环境,可以称为dev,一个是测试环境,我们称为test,最后还要有生产环境,称为prod.每个环境的配置都是不一样的,如何做到快捷方便的使用各自环境的配置绝对是开发中需要着重考虑的. 一.如何配置多环境 在spr

  • Java定时任务的三种实现方法

    译者注:个人觉得用定时任务来跑垃圾回收不是很好的例子,从译者接触到的项目来看,比较常见的是用定时任务来进行非实时计算,清除临时数据.文件等.在本文里,我会给大家介绍3种不同的实现方法:1.普通thread实现2.TimerTask实现3.ScheduledExecutorService实现 一.普通thread 这是最常见的,创建一个thread,然后让它在while循环里一直运行着,通过sleep方法来达到定时任务的效果.这样可以快速简单的实现,代码如下: 复制代码 代码如下: public

  • 小程序页面动态配置实现方法

    小程序可以根据管理后台配置动态更新展示内容,如实现下面新鲜事栏目图片的来源的可配置,点击跳转url的可配置. 实现方案 1后端创建一个map结构的数据表,表结构如下: 2 管理后台根据不同业务设定不同的key和value,更新数据库 如key:ad1url value:a.png 如果需要更新页面图片,只需更新ad1url对应的value 3 小程序根据对应业务key获取对应的配置项,更新页面显示 技术栈 后端接口服务:SpringBoot Mybatis MySql 管理后台:vue 前端:小

  • springboot整合Quartz实现动态配置定时任务的方法

    前言 在我们日常的开发中,很多时候,定时任务都不是写死的,而是写到数据库中,从而实现定时任务的动态配置,下面就通过一个简单的示例,来实现这个功能. 一.新建一个springboot工程,并添加依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency

  • 使用spring-task定时任务动态配置修改执行时间

    目录 spring-task定时任务动态配置修改执行时间 spring schedule 动态配置执行时间 spring-task定时任务动态配置修改执行时间 因项目需要,几个定时任务需要人为动态设置执行时间,于是乎吧,就查阅相关资料,是可以动态设置的,废话不多说,直接上代码,一目了然. package com.seckill.quartz; import org.springframework.scheduling.Trigger; import org.springframework.sch

  • Java中Quartz高可用定时任务快速入门

    目录 定时任务使用指南 1.引入依赖 2.快速上手 3.手动触发定时任务 4.带参数任务 5.任务并发 6.持久化 定时任务使用指南 如果你想做定时任务,有高可用方面的需求,或者仅仅想入门快,上手简单,那么选用它准没错. 定时任务模块是对Quartz框架进一步封装,使用更加简洁. 1.引入依赖 <dependency> <groupId>xin.altitude.cms</groupId> <artifactId>ucode-cms-quartz</a

随机推荐