spring中使用quartz框架(持久化到数据库+springboot)

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
云数据库 RDS MySQL,高可用系列 2核4GB
简介: spring中使用quartz框架(持久化到数据库+springboot)http://www.bieryun.com/1513.html 本例是在springboot中通过读取数据库的定时任务信息,动态生成quartz定时任务 1、导入依赖: [html] view plai...

spring中使用quartz框架(持久化到数据库+springboot)

本例是在springboot中通过读取数据库的定时任务信息,动态生成quartz定时任务

1、导入依赖:

[html] view plain copy

  1. <dependency>
  2.             <groupId>org.quartz-scheduler</groupId>
  3.             <artifactId>quartz</artifactId>
  4.             <version>2.2.1</version>
  5.         </dependency>
  6.         <dependency>
  7.              <groupId>org.springframework</groupId>
  8.              <artifactId>spring-context-support</artifactId>
  9.              <version>${spring.version}</version>
  10.         </dependency>

2、在项目中添加quartz.properties文件(这样就不会走它自带的properties文件)

[html] view plain copy

  1. # Default Properties file for use by StdSchedulerFactory
  2. # to create a Quartz Scheduler Instance, if a different
  3. # properties file is not explicitly specified.
  4. #
  5. #默认或是自己改名字都行
  6. org.quartz.scheduler.instanceName: DefaultQuartzScheduler
  7. #如果使用集群,instanceId必须唯一,设置成AUTO
  8. org.quartz.scheduler.instanceId = AUTO
  9. org.quartz.scheduler.rmi.export: false
  10. org.quartz.scheduler.rmi.proxy: false
  11. org.quartz.scheduler.wrapJobExecutionInUserTransaction: false
  12. org.quartz.threadPool.class: org.quartz.simpl.SimpleThreadPool
  13. org.quartz.threadPool.threadCount: 10
  14. org.quartz.threadPool.threadPriority: 5
  15. org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread: true
  16. org.quartz.jobStore.misfireThreshold: 60000
  17. #============================================================================
  18. # Configure JobStore
  19. #============================================================================
  20. #
  21. #org.quartz.jobStore.class: org.quartz.simpl.RAMJobStore
  22. #存储方式使用JobStoreTX,也就是数据库
  23. org.quartz.jobStore.class:org.quartz.impl.jdbcjobstore.JobStoreTX
  24. org.quartz.jobStore.driverDelegateClass:org.quartz.impl.jdbcjobstore.StdJDBCDelegate
  25. #使用自己的配置文件
  26. org.quartz.jobStore.useProperties:true
  27. #数据库中quartz表的表名前缀
  28. org.quartz.jobStore.tablePrefix:qrtz_
  29. org.quartz.jobStore.dataSource:qzDS
  30. #是否使用集群(如果项目只部署到 一台服务器,就不用了)
  31. org.quartz.jobStore.isClustered = true
  32. #============================================================================
  33. # Configure Datasources
  34. #============================================================================
  35. #配置数据源
  36. org.quartz.dataSource.qzDS.driver:com.mysql.jdbc.Driver
  37. org.quartz.dataSource.qzDS.URL:jdbc:mysql://10.4.33.251:3306/ecif_orgin
  38. org.quartz.dataSource.qzDS.user:reader1
  39. org.quartz.dataSource.qzDS.password:Reader12341
  40. org.quartz.dataSource.qzDS.maxConnection:10

 

3、在数据库中创建quartz相关的表

1)进入quartz的官网http://www.quartz-scheduler.org/,点击Downloads,下载后在目录\docs\dbTables下有常用数据库创建quartz表的脚本。

2)百度去搜创建quartz表

4、自定义MyJobFactory,解决spring不能在quartz中注入bean的问题

[java] view plain copy

  1. package com.nnfe.ecif.domain.bean;
  2. import org.quartz.spi.TriggerFiredBundle;
  3. import org.springframework.beans.factory.annotation.Autowired;
  4. import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
  5. import org.springframework.scheduling.quartz.AdaptableJobFactory;
  6. import org.springframework.stereotype.Component;
  7. @Component
  8. public class MyJobFactory extends AdaptableJobFactory {
  9.       @Autowired
  10.       private AutowireCapableBeanFactory capableBeanFactory;
  11.       @Override
  12.       protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
  13.         Object jobInstance = super.createJobInstance(bundle);
  14.         capableBeanFactory.autowireBean(jobInstance); //这一步解决不能spring注入bean的问题
  15.         return jobInstance;
  16.       }
  17. }

5、创建调度器schedule

[java] view plain copy

  1. package com.nnfe.ecif.config;
  2. import java.io.IOException;
  3. import java.util.Properties;
  4. import org.quartz.Scheduler;
  5. import org.quartz.SchedulerException;
  6. import org.quartz.SchedulerFactory;
  7. import org.quartz.impl.StdSchedulerFactory;
  8. import org.springframework.beans.factory.annotation.Autowired;
  9. import org.springframework.beans.factory.config.PropertiesFactoryBean;
  10. import org.springframework.context.annotation.Bean;
  11. import org.springframework.context.annotation.Configuration;
  12. import org.springframework.core.io.ClassPathResource;
  13. import org.springframework.scheduling.quartz.SchedulerFactoryBean;
  14. import com.nnfe.ecif.domain.bean.MyJobFactory;
  15. @Configuration
  16. public class QuartzConfigration {
  17.      @Autowired
  18.      private MyJobFactory myJobFactory;  //自定义的factory

[java] view plain copy

  1. //获取工厂bean 
  2.     @Bean
  3.     public SchedulerFactoryBean schedulerFactoryBean() {
  4.       SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
  5.       try {
  6.         schedulerFactoryBean.setQuartzProperties(quartzProperties());
  7.         schedulerFactoryBean.setJobFactory(myJobFactory);
  8.     } catch (IOException e) {
  9.         // TODO Auto-generated catch block
  10.         e.printStackTrace();
  11.     }
  12.       return schedulerFactoryBean;
  13.     }

[java] view plain copy

  1. //指定quartz.properties
  2.     @Bean
  3.     public Properties quartzProperties() throws IOException {
  4.         PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
  5.         propertiesFactoryBean.setLocation(new ClassPathResource("/quartz.properties"));
  6.         propertiesFactoryBean.afterPropertiesSet();
  7.         return propertiesFactoryBean.getObject();
  8.     }
  9. //创建schedule  
  10.     @Bean(name = "scheduler")
  11.     public Scheduler scheduler() {
  12.       return schedulerFactoryBean().getScheduler();
  13.     }
  14. }

 

6、更新quartz中的任务

首先我们需要自己创建一张表,用来存放trigger的信息,然后从数据库读取这些信息来随时更新定时任务

现在我的数据库中有两个定时任务,注意:job_name存放的任务类的全路径,在quartz中通过jobName和jobGroup来确定trigger的唯一性,所以这两列为联合唯一索引。

接着创建实体类:

[java] view plain copy

  1. import javax.persistence.Column;
  2. import javax.persistence.Entity;
  3. import javax.persistence.GeneratedValue;
  4. import javax.persistence.GenerationType;
  5. import javax.persistence.Id;
  6. import javax.persistence.Table;
  7. import org.hibernate.annotations.GenericGenerator;
  8. @Entity
  9. @Table(name = "c_schedule_triggers")
  10. public class CScheduleTrigger {
  11.     @Id
  12.     @GenericGenerator(name = "mysqlNative", strategy = "native")
  13.     @GeneratedValue(generator = "mysqlNative")
  14.       private Integer id;
  15.       @Column
  16.       private String cron;  //时间表达式
  17.       private String status; //使用状态 0:禁用   1:启用
  18.       private String jobName; //任务名称
  19.       private String jobGroup; //任务分组

 

更新quartz中的任务

[html] view plain copy

  1. package com.nnfe.ecif.domain.service.impl;
  2. import java.util.List;
  3. import org.quartz.CronScheduleBuilder;
  4. import org.quartz.CronTrigger;
  5. import org.quartz.Job;
  6. import org.quartz.JobBuilder;
  7. import org.quartz.JobDetail;
  8. import org.quartz.JobKey;
  9. import org.quartz.Scheduler;
  10. import org.quartz.TriggerBuilder;
  11. import org.quartz.TriggerKey;
  12. import org.slf4j.Logger;
  13. import org.slf4j.LoggerFactory;
  14. import org.springframework.beans.factory.annotation.Autowired;
  15. import org.springframework.context.annotation.Configuration;
  16. import org.springframework.scheduling.annotation.EnableScheduling;
  17. import org.springframework.scheduling.annotation.Scheduled;
  18. import org.springframework.stereotype.Component;
  19. import org.springframework.stereotype.Service;
  20. import com.nnfe.ecif.domain.orm.w.CScheduleTriggerRepository;
  21. import com.nnfe.ecif.domain.orm.w.CScheduleTrigger;
  22. import com.nnfe.ecif.domain.service.ScheduleTriggerService;
  23. @Service
  24. public class ScheduleTriggerServiceImpl implements ScheduleTriggerService{
  25.     private static final Logger logger =  LoggerFactory.getLogger(ScheduleTriggerServiceImpl.class);
  26.     @Autowired
  27.     private Scheduler scheduler;
  28.     @Autowired
  29.     private CScheduleTriggerRepository triggerRepository;
  30.     @Override
  31.     @Scheduled(cron="0 0 23:00 * * ?")  //每天晚上11点调用这个方法来更新quartz中的任务
  32.     public void refreshTrigger() {
  33.         try {
  34.             //查询出数据库中所有的定时任务
  35.             List<CScheduleTrigger> jobList = triggerRepository.queryAll();
  36.             if(jobList!=null){
  37.                 for(CScheduleTrigger scheduleJob : jobList){
  38.                     String status = scheduleJob.getStatus(); //该任务触发器目前的状态
  39.                     TriggerKey triggerKey = TriggerKey.triggerKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
  40.                     CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
  41.                      //说明本条任务还没有添加到quartz中
  42.                     if (null == trigger) {
  43.                         if(status.equals("0")){ //如果是禁用,则不用创建触发器
  44.                             continue;
  45.                         }
  46.                         JobDetail jobDetail=null;
  47.                         try {
  48.                             //创建JobDetail(数据库中job_name存的任务全路径,这里就可以动态的把任务注入到JobDetail中)
  49.                             jobDetail = JobBuilder.newJob((Class<? extends Job>) Class.forName(scheduleJob.getJobName()))
  50.                                 .withIdentity(scheduleJob.getJobName(), scheduleJob.getJobGroup()).build();
  51.                             //表达式调度构建器
  52.                             CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob
  53.                                 .getCron());
  54.                             //按新的cronExpression表达式构建一个新的trigger
  55.                             trigger = TriggerBuilder.newTrigger().withIdentity(scheduleJob.getJobName(), scheduleJob.getJobGroup()).withSchedule(scheduleBuilder).build();
  56.                             //把trigger和jobDetail注入到调度器
  57.                             scheduler.scheduleJob(jobDetail, trigger);
  58.                         } catch (ClassNotFoundException e) {
  59.                             // TODO Auto-generated catch block
  60.                             e.printStackTrace();
  61.                         }
  62.                     } else {  //说明查出来的这条任务,已经设置到quartz中了
  63.                         // Trigger已存在,先判断是否需要删除,如果不需要,再判定是否时间有变化
  64.                         if(status.equals("0")){ //如果是禁用,从quartz中删除这条任务
  65.                             JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
  66.                             scheduler.deleteJob(jobKey);
  67.                             continue;
  68.                         }
  69.                         String searchCron = scheduleJob.getCron(); //获取数据库的
  70.                         String currentCron = trigger.getCronExpression();
  71.                         if(!searchCron.equals(currentCron)){  //说明该任务有变化,需要更新quartz中的对应的记录
  72.                             //表达式调度构建器
  73.                             CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(searchCron);
  74.                             //按新的cronExpression表达式重新构建trigger
  75.                             trigger = trigger.getTriggerBuilder().withIdentity(triggerKey)
  76.                                 .withSchedule(scheduleBuilder).build();
  77.                             //按新的trigger重新设置job执行
  78.                             scheduler.rescheduleJob(triggerKey, trigger);
  79.                         }
  80.                     }
  81.                 }
  82.             }
  83.         } catch (Exception e) {
  84.            logger.error("定时任务每日刷新触发器任务异常,在ScheduleTriggerServiceImpl的方法refreshTrigger中,异常信息:",e);
  85.         }
  86.     }

 

7、自定义任务

[html] view plain copy

  1. @Component
  2. public class MyTask1 implements Job{
  3.     //这里就可以通过spring注入bean了
  4.     @Autowired
  5.     private CScheduleTriggerRepository jobRepository;
  6.     @Autowired
  7.     private CScheduleRecordsRepository recordsRepository;
  8.     @Override
  9.     public void execute(JobExecutionContext context)
  10.             throws JobExecutionException {
  11.         boolean isExecute = false;  //是否已执行业务逻辑
  12.         boolean flag = false;  //业务逻辑执行后返回结果
  13.         try{
  14.             //可以通过context拿到执行当前任务的quartz中的很多信息,如当前是哪个trigger在执行该任务
  15.             CronTrigger trigger = (CronTrigger) context.getTrigger();
  16.             String corn = trigger.getCronExpression();
  17.             String jobName = trigger.getKey().getName();
  18.             String jobGroup = trigger.getKey().getGroup();

 

要搞清楚一个问题:从数据库读取任务信息动态生成定时任务,和把quartz持久化到数据库是没有关系的。

前者是我们自己定义的业务表,而后者是quartz使用自己的表来存储信息。持久化到数据库后,就算服务器重启或是多个quartz节点也没关系,因为他们共享数据库中的任务信息。

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
2月前
|
NoSQL Java Redis
redis的基本命令,并用netty操作redis(不使用springboot或者spring框架)就单纯的用netty搞。
这篇文章介绍了Redis的基本命令,并展示了如何使用Netty框架直接与Redis服务器进行通信,包括设置Netty客户端、编写处理程序以及初始化Channel的完整示例代码。
59 1
redis的基本命令,并用netty操作redis(不使用springboot或者spring框架)就单纯的用netty搞。
|
2月前
|
Java 测试技术 开发者
springboot学习四:Spring Boot profile多环境配置、devtools热部署
这篇文章主要介绍了如何在Spring Boot中进行多环境配置以及如何整合DevTools实现热部署,以提高开发效率。
89 2
|
2月前
|
前端开发 Java 程序员
springboot 学习十五:Spring Boot 优雅的集成Swagger2、Knife4j
这篇文章是关于如何在Spring Boot项目中集成Swagger2和Knife4j来生成和美化API接口文档的详细教程。
138 1
|
2月前
|
Java API Spring
springboot学习七:Spring Boot2.x 拦截器基础入门&实战项目场景实现
这篇文章是关于Spring Boot 2.x中拦截器的入门教程和实战项目场景实现的详细指南。
29 0
springboot学习七:Spring Boot2.x 拦截器基础入门&实战项目场景实现
|
2月前
|
Java API Spring
springboot学习六:Spring Boot2.x 过滤器基础入门&实战项目场景实现
这篇文章是关于Spring Boot 2.x中过滤器的基础知识和实战项目应用的教程。
31 0
springboot学习六:Spring Boot2.x 过滤器基础入门&实战项目场景实现
|
2月前
|
Java 测试技术 Spring
springboot学习三:Spring Boot 配置文件语法、静态工具类读取配置文件、静态工具类读取配置文件
这篇文章介绍了Spring Boot中配置文件的语法、如何读取配置文件以及如何通过静态工具类读取配置文件。
90 0
springboot学习三:Spring Boot 配置文件语法、静态工具类读取配置文件、静态工具类读取配置文件
|
2月前
|
存储 缓存 API
LangChain-18 Caching 将回答内容进行缓存 可在内存中或数据库中持久化缓存
LangChain-18 Caching 将回答内容进行缓存 可在内存中或数据库中持久化缓存
42 6
|
2月前
|
Java 数据库 数据安全/隐私保护
Spring 微服务提示:使用环境变量抽象数据库主机名
Spring 微服务提示:使用环境变量抽象数据库主机名
45 1
|
2月前
|
SQL Java 数据库
Springboot+spring-boot-starter-data-jdbc实现数据库的操作
本文介绍了如何使用Spring Boot的spring-boot-starter-data-jdbc依赖来操作数据库,包括添加依赖、配置数据库信息和编写基于JdbcTemplate的数据访问代码。
133 2
|
2月前
|
XML Java 应用服务中间件
【Spring】运行Spring Boot项目,请求响应流程分析以及404和500报错
【Spring】运行Spring Boot项目,请求响应流程分析以及404和500报错
199 2