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

简介: 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节点也没关系,因为他们共享数据库中的任务信息。

相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。 &nbsp; 相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情:&nbsp;https://www.aliyun.com/product/rds/mysql&nbsp;
相关文章
|
前端开发 Java 应用服务中间件
《深入理解Spring》 Spring Boot——约定优于配置的革命者
Spring Boot基于“约定优于配置”理念,通过自动配置、起步依赖、嵌入式容器和Actuator四大特性,简化Spring应用的开发与部署,提升效率,降低门槛,成为现代Java开发的事实标准。
|
前端开发 Java 微服务
《深入理解Spring》:Spring、Spring MVC与Spring Boot的深度解析
Spring Framework是Java生态的基石,提供IoC、AOP等核心功能;Spring MVC基于其构建,实现Web层MVC架构;Spring Boot则通过自动配置和内嵌服务器,极大简化了开发与部署。三者层层演进,Spring Boot并非替代,而是对前者的高效封装与增强,适用于微服务与快速开发,而深入理解Spring Framework有助于更好驾驭整体技术栈。
|
XML Java 应用服务中间件
【SpringBoot(一)】Spring的认知、容器功能讲解与自动装配原理的入门,带你熟悉Springboot中基本的注解使用
SpringBoot专栏开篇第一章,讲述认识SpringBoot、Bean容器功能的讲解、自动装配原理的入门,还有其他常用的Springboot注解!如果想要了解SpringBoot,那么就进来看看吧!
978 2
|
人工智能 Java 机器人
基于Spring AI Alibaba + Spring Boot + Ollama搭建本地AI对话机器人API
Spring AI Alibaba集成Ollama,基于Java构建本地大模型应用,支持流式对话、knife4j接口可视化,实现高隐私、免API密钥的离线AI服务。
7881 2
基于Spring AI Alibaba + Spring Boot + Ollama搭建本地AI对话机器人API
|
缓存 Java 应用服务中间件
Spring Boot配置优化:Tomcat+数据库+缓存+日志,全场景教程
本文详解Spring Boot十大核心配置优化技巧,涵盖Tomcat连接池、数据库连接池、Jackson时区、日志管理、缓存策略、异步线程池等关键配置,结合代码示例与通俗解释,助你轻松掌握高并发场景下的性能调优方法,适用于实际项目落地。
2081 5
存储 JSON Java
1133 0
|
人工智能 Java 开发者
【Spring】原理解析:Spring Boot 自动配置
Spring Boot通过“约定优于配置”的设计理念,自动检测项目依赖并根据这些依赖自动装配相应的Bean,从而解放开发者从繁琐的配置工作中解脱出来,专注于业务逻辑实现。
3160 0
|
安全 Java Nacos
0代码改动实现Spring应用数据库帐密自动轮转
Nacos作为国内被广泛使用的配置中心,已经成为应用侧的基础设施产品,近年来安全问题被更多关注,这是中国国内软件行业逐渐迈向成熟的标志,也是必经之路,Nacos提供配置加密存储-运行时轮转的核心安全能力,将在应用安全领域承担更多职责。
|
SQL XML Java
配置Spring框架以连接SQL Server数据库
最后,需要集成Spring配置到应用中,这通常在 `main`方法或者Spring Boot的应用配置类中通过加载XML配置或使用注解来实现。
892 0