这种方式整合Quartz你见过吗?

知识改变命运,撸码使我快乐,2019年你的发迹线还好吗?

点赞再看,养成习惯

本篇文章对应源码码云(Gitee)仓库

https://gitee.com/minbox-projects/api-boot-chapter,您的Star是给我最大动力

Quartz是一款优秀的任务调度框架,支持内存、JDBC的形式来存储未执行的任务列表,支持多个任务节点同时执行任务,支持任务漂移到不同的节点执行。

前言

这么优秀的任务调度框架我想应该是很多开发者的首选解决方案,因此ApiBoot对它下手了,基于SpringBoot封装了名为ApiBoot Quartz的组件,同样是通过application.yml配置文件的形式就可以简单的实现初始化集成。

如果使用默认的配置,我们可以不编写一行集成相关的代码,ApiBoot Quartz还针对日常高频率使用的方法提供了一个接口,定义的方法如:创建任务、暂停任务、恢复任务、删除任务等等。

本文所需ApiBoot相关链接:

任务存储方式

Quartz自身提供了两种存储任务的方式:

  • Memory:内存方式,将任务存储到内存中,当项目重启时就会丢失,不建议生产环境使用。
  • Jdbc:数据库方式,将任务存储到Quartz提供的固定结构的表内,项目重启任务不会丢失,多种数据库的建表语句请访问:Quartz Schemas 按需选择。

ApiBootQuartz内提供的两种存储方式进行了封装,通过api.boot.quartz.job-store-type参数进行配置,该参数默认值为memory,所以你如果使用内存方式该参数不需要修改,更多配置请访问 ApiBootQuartzProperties 查看。

任务类型

任务类型是ApiBoot Quartz的新概念,其实在Quartz中任务并没有类型区分,实现org.quartz.Job接口就可以创建一个任务。

不过Spring也是爱折腾,对其进行了封装提供了QuartzJobBean,它是一个抽象类,我们继承该类后也可以创建一个定时任务。

ApiBoot Quartz中有三种任务类型,分别是:

  • OnceJob:仅执行一次的任务类型
  • CronJob:使用Cron表达式来定义任务的执行周期
  • LoopJob:可指定循环次数的任务,根据指定循环的次数进行重复执行

内置方法

ApiBoot封装Quartz后所提供的方法都位于 ApiBootQuartzService 接口中,而该接口有一个默认的实现类 ApiBootQuartzServiceDefaultSupport ,该实现类全部实现了接口定义方法,内部通过org.quartz.Scheduler来实现任务的基本操作。

内置方法列表:

方法 描述
Scheduler getScheduler(); 获取SpringIoc容器内的Scheduler实例
String newJob(ApiBootJobWrapper jobWrapper); 通过封装的对象创建一个新的任务,这是创建所有类型任务的入口
void deleteJob(String jobKey); 删除一个任务
void deleteJobs(String... jobKeys); 删除一系列任务
void deleteJobs(Collection<String> jobKeys); 删除集合内的所有任务
void pauseJob(String jobKey); 暂停一个任务
void pauseJobs(String... jobKeys); 暂停传递的所有任务
void pauseJobs(Collection<String> jobKeys); 暂停集合内的所有任务
void resumeJob(String jobKey); 恢复一个任务执行
void resumeJobs(String... jobKeys); 恢复数组内的所有任务执行
void resumeJobs(Collection<String> jobKeys); 恢复集合内的所有任务执行
void updateJobCron(String jobKey, String cron); 更新任务Cron表达式
void updateJobStartTime(String jobKey, Date jobStartTime); 更新任务开始时间
void startAllJobs(); 启动所有定时任务
void shutdownAllJobs(); 关闭所有定时任务

既然ApiBoot为我们提供了这么多内置的方法,我们接下来创建一个项目来感受一下。

示例项目

使用Idea创建一个SpringBoot项目,我们把本章所需要的依赖添加在pom.xml文件内,如下所示:

<dependencies>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
  </dependency>
  <!--ApiBoot Quartz-->
  <dependency>
    <groupId>org.minbox.framework</groupId>
    <artifactId>api-boot-starter-quartz</artifactId>
  </dependency>
</dependencies>
<dependencyManagement>
  <dependencies>
    <!--ApiBoot统一版本-->
    <dependency>
      <groupId>org.minbox.framework</groupId>
      <artifactId>api-boot-dependencies</artifactId>
      <version>2.2.1.RELEASE</version>
      <scope>import</scope>
      <type>pom</type>
    </dependency>
  </dependencies>
</dependencyManagement>

注意:ApiBoot的版本是根据SpringBoot的版本而定义的,详见 ApiBoot 版本分支

示例任务类

我们来创建一个名为DemoJob的任务调度示例类,如下所示:

/**
 * 示例任务
 *
 * @author 恒宇少年
 */
public class DemoJob extends QuartzJobBean {
    @Override
    protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        System.out.println("任务执行了..");
    }
}

QuartzJobBean是什么?

QuartzJobBean是由Spring提供,实现org.quartz.Job接口,是对Quartz内置任务接口的实现并且封装。

QuartzJobBean的优势

Spring所提供的QuartzJobBean具体有什么优势呢?

  • 自动将实现类实例加入IOC

    使用QuartzJobBean来创建自定义任务时,Spring会自动扫描项目内的实现类,将每一个实现类通过反射机制创建出实例并将实例写入到IOC容器内。

  • 可在实现类内注入实例

    直接使用Quartz时,如果自定义任务类实例不加入IOC容器,我们无法在自定义任务类注入Service,这一点了解Spring基础的同学应该都明白,我们无法在非被IOC托管的类内进行注入操作,而使用QuartzJobBean则不用考虑这一点。

QuartzJobBean注册流程

使用ApiBoot Quartz后为什么不需要再手动添加注入把任务实现类加入到IOC容器?

任务类注册流程如下所示:

  • 第一步:ApiBootQuartzAutoConfiguration#quartzScheduler()

    ApiBoot在集成Quartz时提供了一个自动化配置类 ApiBootQuartzAutoConfiguration ,在该类中通过quartzScheduler()方法来自动创建一个SchedulerFactoryBean实例

  • 第二步:SchedulerFactoryBean#setJobFactory()

    通过SchedulerFactoryBean内提供的setJobFactory()方法可以自定义设置具体使用JobFactory的实现类,而在spring-context-support依赖内已经提供了相关实现,名为 SpringBeanJobFactory

  • 第三步:SpringBeanJobFactory#createJobInstance()

    在项目启动时会将扫描到的所有QuartzJobBean实现类通过JobFactory#newJob方法进行创建任务实例后将实例交付给Quartz框架进行准备后期的任务调度。

SpringBeanJobFactory类继承于AdaptableJobFactory类,而AdaptableJobFactory则是实现了JobFactory接口,这样的话如果我们将SpringBeanJobFactory设置给SchedulerFactoryBean项目启动时Quartz就会调用SpringBeanJobFactory#createJobInstance()方法来创建任务实例。

而在createJobInstance()方法内Spring则是将创建的任务实例存入了IOC容器内,这样一来我们的自定义任务内就可以进行注入其他Bean的操作了,该方法源码如下所示:

/**
    * Create the job instance, populating it with property values taken
    * from the scheduler context, job data map and trigger data map.
    */
@Override
protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
  Object job = (this.applicationContext != null ?
                // 通过ApplicationContext创建任务实例,并添加到IOC
                this.applicationContext.getAutowireCapableBeanFactory().createBean(
                  bundle.getJobDetail().getJobClass(), AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR, false) :
                super.createJobInstance(bundle));

  if (isEligibleForPropertyPopulation(job)) {
    BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(job);
    MutablePropertyValues pvs = new MutablePropertyValues();
    if (this.schedulerContext != null) {
      pvs.addPropertyValues(this.schedulerContext);
    }
    pvs.addPropertyValues(bundle.getJobDetail().getJobDataMap());
    pvs.addPropertyValues(bundle.getTrigger().getJobDataMap());
    if (this.ignoredUnknownProperties != null) {
      for (String propName : this.ignoredUnknownProperties) {
        if (pvs.contains(propName) && !bw.isWritableProperty(propName)) {
          pvs.removePropertyValue(propName);
        }
      }
      bw.setPropertyValues(pvs);
    }
    else {
      bw.setPropertyValues(pvs, true);
    }
  }

  return job;
}

大致的QuartzJobBean实现类注册流程就是这个样子的,下面让我们来见识下是不是真的有那么简单就可以创建并执行一个任务。

运行测试

为了演示方便,我们修改XxxApplication入口类,让项目启动后自动执行DemoJob任务,如下所示:

@SpringBootApplication
public class ApibootQuartzIntegratedAwayApplication implements CommandLineRunner {

  public static void main(String[] args) {
    SpringApplication.run(ApibootQuartzIntegratedAwayApplication.class, args);
  }

  /**
    * ApiBoot Quartz内置方法接口
    * {@link org.minbox.framework.api.boot.plugin.quartz.support.ApiBootQuartzServiceDefaultSupport}
    */
  @Autowired
  private ApiBootQuartzService quartzService;

  @Override
  public void run(String... args) throws Exception {
    quartzService.newJob(ApiBootOnceJobWrapper.Context()
                         .jobClass(DemoJob.class)
                         .wrapper());
  }
}

CommandLineRunner是由SpringBoot提供用于执行项目启动后的逻辑。

启动项目后控制台输出内容如下所示:

......
任务执行了..

心细的同学应该看到了我们使用了ApiBootOnceJobWeapper这个封装类来创建的任务对象,我们下一章就来讲下这个封装类到底可以干什么?

敲黑板,划重点

Quartz是一个优秀的分布式任务调度框架,ApiBoot封装了它,使它插上了翅膀,让我们明白了简单的另一层定义。

代码示例

如果您喜欢本篇文章请为源码仓库点个Star,谢谢!!!
本篇文章示例源码可以通过以下途径获取,目录为apiboot-quartz-integrated-away

作者个人 博客
使用开源框架 ApiBoot 助你成为Api接口服务架构师

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343