背景

最近在做项目,项目中有个需求:需要使用定时任务,这个定时任务需要即时生效。
查看Quartz官网之后发现:Quartz提供两种基本作业存储类型:

  • RAMJobStore :RAM也就是内存,默认情况下Quartz会将任务调度存在内存中,这种方式性能是最好的,因为内存的速度是最快的。不好的地方就是数据缺乏持久性,但程序崩溃或者重新发布的时候,所有运行信息都会丢失
  • JDBC作业存储:存到数据库之后,可以做单点也可以做集群,当任务多了之后,可以统一进行管理。关闭或者重启服务器,运行的信息都不会丢失。缺点就是运行速度快慢取决于连接数据库的快慢。

SpringBoot集成Quartz

我们也可以自己去将quartz和springBoot整合在一起,其实说是springBoot还不如说是sping,因为我们没有用到spirngboot的相关的快捷方式。
如果童鞋们想快速集成Quartz,立刻看到效果的话,可以直接往下翻,直接看SpirngBoot自带的Quartz插件。但我建议大家还是从spring整合Quartz开始,懂的原理,方有收获。

Quartz初始化表

如果需要做持久化的话,数据肯定是要存在数据库的,那么到底存在哪些表呢?其实官网文档也跟我们讲过了,地址如下:
http://www.quartz-scheduler.org/documentation/quartz-2.2.x/tutorials/tutorial-lesson-09.html
其中有句话:

JDBCJobStore works with nearly any database, it has been used widely with Oracle, PostgreSQL, MySQL, MS SQLServer, HSQLDB, and DB2. To use JDBCJobStore, you must first create a set of database tables for Quartz to use. You can find table-creation SQL scripts in the “docs/dbTables” directory of the Quartz distribution.
大概就是支持这么多的数据库类型。如果你要使用JDBCJoBStore的话,你先要创建一些表,这些表在 “doc/dbTables”里面。“doc/dbTables” 在哪儿呢?其实都在源码里面,直接到官网下下来就行了。

Spring整合Quartz

pom文件

  1. <dependencies>
  2. <dependency>
  3. <groupId>org.springframework.boot</groupId>
  4. <artifactId>spring-boot-starter</artifactId>
  5. </dependency>
  6. <dependency>
  7. <groupId>org.springframework.boot</groupId>
  8. <artifactId>spring-boot-starter-test</artifactId>
  9. <scope>test</scope>
  10. </dependency>
  11. <!--quartz -->
  12. <dependency>
  13. <groupId>org.quartz-scheduler</groupId>
  14. <artifactId>quartz</artifactId>
  15. </dependency>
  16. <dependency>
  17. <groupId>org.quartz-scheduler</groupId>
  18. <artifactId>quartz-jobs</artifactId>
  19. <!-- <version>2.3.0</version> -->
  20. </dependency>
  21. <!--定时任务需要依赖context模块-->
  22. <dependency>
  23. <groupId>org.springframework</groupId>
  24. <artifactId>spring-context-support</artifactId>
  25. </dependency>
  26. <dependency>
  27. <groupId>org.springframework.boot</groupId>
  28. <artifactId>spring-boot-starter-web</artifactId>
  29. </dependency>
  30. <dependency>
  31. <groupId>mysql</groupId>
  32. <artifactId>mysql-connector-java</artifactId>
  33. <scope>runtime</scope>
  34. </dependency>
  35. <dependency>
  36. <groupId>org.mybatis.spring.boot</groupId>
  37. <artifactId>mybatis-spring-boot-starter</artifactId>
  38. <version>1.3.1</version>
  39. </dependency>
  40. <dependency>
  41. <groupId>com.mchange</groupId>
  42. <artifactId>c3p0</artifactId>
  43. <version>0.9.5.2</version>
  44. </dependency>
  45. <!--&lt;!&ndash; druid数据库连接池 &ndash;&gt;-->
  46. <dependency>
  47. <groupId>com.alibaba</groupId>
  48. <artifactId>druid-spring-boot-starter</artifactId>
  49. <version>1.1.10</version>
  50. </dependency>
  51. </dependencies>

对应的properties 文件

  1. #使用自己的配置文件
  2. org.quartz.jobStore.useProperties:true
  3.  
  4. #默认或是自己改名字都行
  5. org.quartz.scheduler.instanceName: DefaultQuartzScheduler
  6. #如果使用集群,instanceId必须唯一,设置成AUTO
  7. org.quartz.scheduler.instanceId = AUTO
  8.  
  9. org.quartz.threadPool.class: org.quartz.simpl.SimpleThreadPool
  10. org.quartz.threadPool.threadCount: 10
  11. org.quartz.threadPool.threadPriority: 5
  12. org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread: true
  13.  
  14. #存储方式使用JobStoreTX,也就是数据库
  15. org.quartz.jobStore.class:org.quartz.impl.jdbcjobstore.JobStoreTX
  16. org.quartz.jobStore.driverDelegateClass:org.quartz.impl.jdbcjobstore.StdJDBCDelegate
  17. #是否使用集群(如果项目只部署到 一台服务器,就不用了)
  18. org.quartz.jobStore.isClustered = false
  19. org.quartz.jobStore.clusterCheckinInterval=20000
  20. org.quartz.jobStore.tablePrefix = qrtz_
  21. org.quartz.jobStore.dataSource = myDS
  22.  
  23. #配置数据源
  24. #数据库中quartz表的表名前缀
  25. org.quartz.dataSource.myDS.driver = com.mysql.jdbc.Driver
  26. org.quartz.dataSource.myDS.URL = jdbc:mysql://localhost:3306/aipyun?serverTimezone=GMT&characterEncoding=utf-8
  27. org.quartz.dataSource.myDS.user = root
  28. org.quartz.dataSource.myDS.password = root123
  29. org.quartz.dataSource.myDS.maxConnections = 5

核心QuartzConfiguration类:

  1. ackage com.cj.config;
  2.  
  3. import org.quartz.Scheduler;
  4. import org.quartz.spi.JobFactory;
  5. import org.springframework.beans.factory.annotation.Autowired;
  6. import org.springframework.context.annotation.Bean;
  7. import org.springframework.context.annotation.Configuration;
  8. import org.springframework.core.io.ClassPathResource;
  9. import org.springframework.scheduling.quartz.SchedulerFactoryBean;
  10.  
  11. /**
  12. * 描述 : quartz 配置信息
  13. *
  14. * @author caojing
  15. * @create 2018-12-24-16:47
  16. */
  17. @Configuration
  18. public class QuartzConfiguration {
  19. @Autowired
  20. private JobFactory jobFactory;
  21.  
  22. @Bean
  23. public SchedulerFactoryBean schedulerFactoryBean() {
  24. SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
  25. schedulerFactoryBean.setJobFactory(jobFactory);
  26. // 用于quartz集群,QuartzScheduler 启动时更新己存在的Job
  27. schedulerFactoryBean.setOverwriteExistingJobs(true);
  28. //延长启动
  29. schedulerFactoryBean.setStartupDelay(1);
  30. //设置加载的配置文件
  31. schedulerFactoryBean.setConfigLocation(new ClassPathResource("/quartz.properties"));
  32. return schedulerFactoryBean;
  33. }
  34.  
  35. @Bean
  36. public Scheduler scheduler() {
  37. return schedulerFactoryBean().getScheduler();
  38. }
  39. }

这其中我们把2个类的初始化移到了IOC中,因为之前Quartz的实例化是自己去控制的,为什么要这么做后面会有讲到。
一个是SchedulerFactoryBean类,这个类其实就是之前xml配置中的SchedulerFactoryBean。附上之前xml配置如下(这里不需要配置,springboot建议我们少用xml配置)

  1. <bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
  2. <property name="triggers">
  3. <list>
  4. <ref bean="oceanStatusCronTrigger"/>
  5. </list>
  6. </property>
  7. </bean>

这个类我相信只要用过xml配置的人一定很熟悉,这是Quartz入口。同时也是spring 和Scheduler 关系的桥梁。以便在Spring容器启动后,Scheduler自动开始工作,而在Spring容器关闭前,自动关闭Scheduler。

JobFactory类

  1. package com.cj.config;
  2.  
  3. import org.quartz.spi.TriggerFiredBundle;
  4. import org.springframework.beans.factory.annotation.Autowired;
  5. import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
  6. import org.springframework.context.ApplicationContext;
  7. import org.springframework.context.ApplicationContextAware;
  8. import org.springframework.scheduling.quartz.AdaptableJobFactory;
  9. import org.springframework.scheduling.quartz.SpringBeanJobFactory;
  10. import org.springframework.stereotype.Component;
  11.  
  12. /**
  13. * 描述:
  14. *
  15. * @author caojing
  16. * @create 2018-12-26-14:03
  17. */
  18. @Component
  19. public class JobFactory extends AdaptableJobFactory {
  20.  
  21. @Autowired
  22. private AutowireCapableBeanFactory capableBeanFactory;
  23.  
  24. @Override
  25. protected Object createJobInstance(final TriggerFiredBundle bundle) throws Exception {
  26. // 调用父类的方法
  27. Object jobInstance = super.createJobInstance(bundle);
  28. // 进行注入
  29. capableBeanFactory.autowireBean(jobInstance);
  30. return jobInstance;
  31. }
  32. }

这个类的作用就是讲Job的实例化交给IOC去进行。
其实问题在于:

Job对象的实例化过程是在Quartz中进行的,注入的实体类是在Spring容器当中的 所以在job中无法注入Srping容器的实体类。

如何纳入:Job的创建都是通过JobFactory创建的。JobFactory 有2个实现类:AdaptableJobFactory 和 SimpleJobFactory:

  • 自定义的工厂类 JobFactory 继承 AdaptableJobFactory 。
  • 通过调用父类 AdaptableJobFactory 的方法createJobInstance来实现对Job的实例化。
  • 在Job实例化完以后,再调用自身方法为创建好的Job实例进行属性自动装配并将其纳入到Spring容器的管理之中。(通过AutowireCapableBeanFactory纳入)。

UploadTask 类:

  1. package com.cj.quartzdemo;
  2. import com.cj.controller.IndexController;
  3. import org.quartz.DisallowConcurrentExecution;
  4. import org.quartz.JobExecutionContext;
  5. import org.quartz.JobExecutionException;
  6. import org.springframework.beans.factory.annotation.Autowired;
  7. import org.springframework.scheduling.quartz.QuartzJobBean;
  8. import org.springframework.stereotype.Component;
  9. import org.springframework.stereotype.Service;
  10.  
  11. import java.util.Date;
  12.  
  13. /**
  14. * 描述:
  15. *
  16. * @author caojing
  17. * @create 2018-12-25-11:38
  18. */
  19. @Component
  20. @DisallowConcurrentExecution
  21. public class UploadTask extends QuartzJobBean {
  22. @Autowired
  23. private IndexController indexController;
  24. @Override
  25. protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
  26. System.out.println(new Date() + "任务开始------------------------------------");
  27. try {
  28. Thread.sleep(10000);
  29. } catch (InterruptedException e) {
  30. e.printStackTrace();
  31. }
  32. System.out.println(new Date() + "任务结束------------------------------------");
  33. }
  34. }

继承QuartzJobBean类,重写executeInternal方法。
附:DisallowConcurrentExecution 比如job执行10秒,任务是每隔5秒执行,加上这个注解,程序就会等10秒结束后再执行下一个任务。

indexController类:

  1. package com.cj.controller;
  2.  
  3. import com.cj.quartzdemo.UploadTask;
  4. import org.quartz.*;
  5. import org.springframework.beans.factory.annotation.Autowired;
  6. import org.springframework.stereotype.Controller;
  7. import org.springframework.web.bind.annotation.RequestMapping;
  8. import org.springframework.web.bind.annotation.RequestMethod;
  9.  
  10. /**
  11. * 描述:
  12. *
  13. * @author caojing
  14. * @create 2018-12-26-14:11
  15. */
  16. @Controller
  17. public class IndexController {
  18. @Autowired
  19. private Scheduler scheduler;
  20.  
  21. @RequestMapping(value = "/index", method = RequestMethod.GET)
  22. public void index() throws SchedulerException {
  23. //cron表达式
  24. CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule("0/8 * * * * ?");
  25. //根据name 和group获取当前trgger 的身份
  26. TriggerKey triggerKey = TriggerKey.triggerKey("cj", "123");
  27. CronTrigger triggerOld = null;
  28. try {
  29. //获取 触发器的信息
  30. triggerOld = (CronTrigger) scheduler.getTrigger(triggerKey);
  31. } catch (SchedulerException e) {
  32. e.printStackTrace();
  33. }
  34. if (triggerOld == null) {
  35. //将job加入到jobDetail中
  36. JobDetail jobDetail = JobBuilder.newJob(UploadTask.class).withIdentity("cj", "123").build();
  37. Trigger trigger = TriggerBuilder.newTrigger().withIdentity("cj","123").withSchedule(cronScheduleBuilder).build();
  38. //执行任务
  39. scheduler.scheduleJob(jobDetail, trigger);
  40. } else {
  41. System.out.println("当前job已存在--------------------------------------------");
  42. }
  43. }
  44. }

浏览器输入 http://localhost:8080/index 就可以看到数据库已经存储了我们写的cron表达式和相应的类。
查看数据库表(qrtz_cron_triggers)附上截图:

至此,job 已经被我们成功持久化到数据库。我们来回顾下整体的一个流程。

pom文件添加对应的依赖。
mysql数据库对应表的初始化。
配置对应的properties
将原来quartz控制的类的实例化交给spirng IOC控制。(对应的是核心QuartzConfiguration类和JobFactory类)
业务逻辑层对job进行控制。

SpringBoot整合Quartz定时任务(持久化到数据库)的更多相关文章

  1. springboot整合quartz并持久化到数据库

    首先,这里的持久化是是如果当服务器宕机时,任务还在为我们保存.并且在启动服务器之后仍然可以自动执行 一.创建quartz 建表语句mysql的,quartz 2.3.0版本 DROP TABLE IF ...

  2. SpringBoot整合Quartz定时任务 系统job Spring Boot教程 调度任务

    原文地址:https://www.cnblogs.com/allalongx/p/8477368.html 构建工程 创建一个Springboot工程,在它的程序入口加上@EnableScheduli ...

  3. SpringBoot整合Quartz定时任务

    记录一个SpringBoot 整合 Quartz 的Demo实例 POM.XML文件 <!-- 定时器任务 quartz需要导入的坐标 --> <dependency> < ...

  4. SpringBoot整合Quartz定时任务 的简单实例 2

    (1)什么是Quartz?(2)Quartz的特点:(3)Quartz专用词汇说明:(4)Quartz任务调度基本实现原理: 接下来看下具体的内容: (1)什么是Quartz? Quartz是一个完全 ...

  5. SpringBoot整合Quartz定时任务 的简单实例

    POM.XML文件 <!-- 定时器任务 quartz需要导入的坐标 --> <dependency> <groupId>org.quartz-scheduler& ...

  6. SpringBoot整合quartz实现动态启动,停止定时任务功能

    注意:这个方法当程序重启之后会失效,所以必须将定时任务持久化到数据库,然后程序启动的时候重新把数据库的定时任务加载到quartz中 springboot程序启动初始化代码参考:https://www. ...

  7. Spring整合Quartz定时任务 在集群、分布式系统中的应用(Mysql数据库环境)

    Spring整合Quartz定时任务 在集群.分布式系统中的应用(Mysql数据库环境)   转载:http://www.cnblogs.com/jiafuwei/p/6145280.html 单个Q ...

  8. Spring整合Quartz定时任务执行2次,Spring定时任务执行2次

    Spring整合Quartz定时任务执行2次,Spring定时任务执行2次 >>>>>>>>>>>>>>>&g ...

  9. SpringBoot整合Quartz及log4j实例

    SpringBoot整合Quartz及log4j实例 因为之前项目中经常会做一些定时Job的东西,所以在此记录一下,目前项目中已经使用elastic-job,这个能相对比Quartz更加简单方便一些, ...

随机推荐

  1. PYTHON 当前.PY文件名不能与引入的模块同名

    当前文件名:sqlite3.py 文件引入import sqlite3 运行会出错,因为调用sqlite3的方法首先从当前文件找方法,当然找不到,所以会报错了

  2. 【LeetCode】61. 旋转链表

    61. 旋转链表 知识点:链表: 题目描述 给你一个链表的头节点 head ,旋转链表,将链表每个节点向右移动 k 个位置. 示例 输入:head = [1,2,3,4,5], k = 2 输出:[4 ...

  3. TypeScript——原始数据类型

    TypeScript原始数据类型 原始数据类型包括:布尔值.数值.字符串.null.undefined. Symbol.BigInt. 布尔值: let isDone: boolean = false ...

  4. MapReduce学习总结之架构

    一.MapReduce1.x架构 1)jobTracker:JT 作业的管理者 将作业分解成一堆任务:Task(MapTask和ReduceTask) 将任务分派给TaskTracker(TT)运行 ...

  5. python编程面试题

    # 实现需求为 注册.登录.查看昵称的功能 # def userN(): #     username = input("请输入账号: \n") #     password =  ...

  6. Html模板引擎Handlerbars使用demo

    1.自定义demo <html> <head> <script src="./handlebars-v4.0.12.js"></scrip ...

  7. 【知识详解】Https详解

    Https详解 1.什么是Https Http + SSL = Https 一句话说:Https是身披SSL的Http,当使用了SSL后,Http先和SSL通信,再由SSL和TCP通信, 2.为什么需 ...

  8. dotnet部署出现Failed to load the dll from [ ... hostfxr.dll], HRESULT: 0x80070057

    起因 最近看到.net core 3支持wpf了,尝试一下(如果可行,会特别利于脱离.net运行时) dotnet new wpf dotnet publish -c Release -r win-x ...

  9. java try_catch 分析

    1.若一段代码前有异常抛出,并且这个异常没有被捕获,这段代码将产生编译时错误「无法访问的语句」.如 public class try_catch_Demo { public static void m ...

  10. 🔥 LeetCode 热题 HOT 100(11-20)

    20. 有效的括号 class Solution { public boolean isValid(String s) { Map<Character, Character> map = ...