spring中使用quartz框架(持久化到数据库+springboot)
本例是在springboot中通过读取数据库的定时任务信息,动态生成quartz定时任务
1、导入依赖:
- <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>
- <version>${spring.version}</version>
- </dependency>
2、在项目中添加quartz.properties文件(这样就不会走它自带的properties文件)
- # Default Properties file for use by StdSchedulerFactory
- # to create a Quartz Scheduler Instance, if a different
- # properties file is not explicitly specified.
- #
- #默认或是自己改名字都行
- org.quartz.scheduler.instanceName: DefaultQuartzScheduler
- #如果使用集群,instanceId必须唯一,设置成AUTO
- org.quartz.scheduler.instanceId = AUTO
- org.quartz.scheduler.rmi.export: false
- org.quartz.scheduler.rmi.proxy: false
- org.quartz.scheduler.wrapJobExecutionInUserTransaction: false
- org.quartz.threadPool.class: org.quartz.simpl.SimpleThreadPool
- org.quartz.threadPool.threadCount: 10
- org.quartz.threadPool.threadPriority: 5
- org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread: true
- org.quartz.jobStore.misfireThreshold: 60000
- #============================================================================
- # Configure JobStore
- #============================================================================
- #
- #org.quartz.jobStore.class: org.quartz.simpl.RAMJobStore
- #存储方式使用JobStoreTX,也就是数据库
- org.quartz.jobStore.class:org.quartz.impl.jdbcjobstore.JobStoreTX
- org.quartz.jobStore.driverDelegateClass:org.quartz.impl.jdbcjobstore.StdJDBCDelegate
- #使用自己的配置文件
- org.quartz.jobStore.useProperties:true
- #数据库中quartz表的表名前缀
- org.quartz.jobStore.tablePrefix:qrtz_
- org.quartz.jobStore.dataSource:qzDS
- #是否使用集群(如果项目只部署到 一台服务器,就不用了)
- org.quartz.jobStore.isClustered = true
- #============================================================================
- # Configure Datasources
- #============================================================================
- #配置数据源
- org.quartz.dataSource.qzDS.driver:com.mysql.jdbc.Driver
- org.quartz.dataSource.qzDS.URL:jdbc:mysql://10.4.33.251:3306/ecif_orgin
- org.quartz.dataSource.qzDS.user:reader1
- org.quartz.dataSource.qzDS.password:Reader12341
- 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的问题
- package com.nnfe.ecif.domain.bean;
- import org.quartz.spi.TriggerFiredBundle;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
- import org.springframework.scheduling.quartz.AdaptableJobFactory;
- import org.springframework.stereotype.Component;
- @Component
- public class MyJobFactory extends AdaptableJobFactory {
- @Autowired
- private AutowireCapableBeanFactory capableBeanFactory;
- @Override
- protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
- Object jobInstance = super.createJobInstance(bundle);
- capableBeanFactory.autowireBean(jobInstance); //这一步解决不能spring注入bean的问题
- return jobInstance;
- }
- }
5、创建调度器schedule
- package com.nnfe.ecif.config;
- import java.io.IOException;
- import java.util.Properties;
- import org.quartz.Scheduler;
- import org.quartz.SchedulerException;
- import org.quartz.SchedulerFactory;
- import org.quartz.impl.StdSchedulerFactory;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.beans.factory.config.PropertiesFactoryBean;
- import org.springframework.context.annotation.Bean;
- import org.springframework.context.annotation.Configuration;
- import org.springframework.core.io.ClassPathResource;
- import org.springframework.scheduling.quartz.SchedulerFactoryBean;
- import com.nnfe.ecif.domain.bean.MyJobFactory;
- @Configuration
- public class QuartzConfigration {
- @Autowired
- private MyJobFactory myJobFactory; //自定义的factory
- //获取工厂bean
- @Bean
- public SchedulerFactoryBean schedulerFactoryBean() {
- SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
- try {
- schedulerFactoryBean.setQuartzProperties(quartzProperties());
- schedulerFactoryBean.setJobFactory(myJobFactory);
- } catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- return schedulerFactoryBean;
- }
- //指定quartz.properties
- @Bean
- public Properties quartzProperties() throws IOException {
- PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
- propertiesFactoryBean.setLocation(new ClassPathResource("/quartz.properties"));
- propertiesFactoryBean.afterPropertiesSet();
- return propertiesFactoryBean.getObject();
- }
- //创建schedule
- @Bean(name = "scheduler")
- public Scheduler scheduler() {
- return schedulerFactoryBean().getScheduler();
- }
- }
6、更新quartz中的任务
首先我们需要自己创建一张表,用来存放trigger的信息,然后从数据库读取这些信息来随时更新定时任务
现在我的数据库中有两个定时任务,注意:job_name存放的任务类的全路径,在quartz中通过jobName和jobGroup来确定trigger的唯一性,所以这两列为联合唯一索引。
接着创建实体类:
- import javax.persistence.Column;
- import javax.persistence.Entity;
- import javax.persistence.GeneratedValue;
- import javax.persistence.GenerationType;
- import javax.persistence.Id;
- import javax.persistence.Table;
- import org.hibernate.annotations.GenericGenerator;
- @Entity
- @Table(name = "c_schedule_triggers")
- public class CScheduleTrigger {
- @Id
- @GenericGenerator(name = "mysqlNative", strategy = "native")
- @GeneratedValue(generator = "mysqlNative")
- private Integer id;
- @Column
- private String cron; //时间表达式
- private String status; //使用状态 0:禁用 1:启用
- private String jobName; //任务名称
- private String jobGroup; //任务分组
更新quartz中的任务
- package com.nnfe.ecif.domain.service.impl;
- import java.util.List;
- import org.quartz.CronScheduleBuilder;
- import org.quartz.CronTrigger;
- import org.quartz.Job;
- import org.quartz.JobBuilder;
- import org.quartz.JobDetail;
- import org.quartz.JobKey;
- import org.quartz.Scheduler;
- import org.quartz.TriggerBuilder;
- import org.quartz.TriggerKey;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.context.annotation.Configuration;
- import org.springframework.scheduling.annotation.EnableScheduling;
- import org.springframework.scheduling.annotation.Scheduled;
- import org.springframework.stereotype.Component;
- import org.springframework.stereotype.Service;
- import com.nnfe.ecif.domain.orm.w.CScheduleTriggerRepository;
- import com.nnfe.ecif.domain.orm.w.CScheduleTrigger;
- import com.nnfe.ecif.domain.service.ScheduleTriggerService;
- @Service
- public class ScheduleTriggerServiceImpl implements ScheduleTriggerService{
- private static final Logger logger = LoggerFactory.getLogger(ScheduleTriggerServiceImpl.class);
- @Autowired
- private Scheduler scheduler;
- @Autowired
- private CScheduleTriggerRepository triggerRepository;
- @Override
- @Scheduled(cron="0 0 23:00 * * ?") //每天晚上11点调用这个方法来更新quartz中的任务
- public void refreshTrigger() {
- try {
- //查询出数据库中所有的定时任务
- List<CScheduleTrigger> jobList = triggerRepository.queryAll();
- if(jobList!=null){
- for(CScheduleTrigger scheduleJob : jobList){
- String status = scheduleJob.getStatus(); //该任务触发器目前的状态
- TriggerKey triggerKey = TriggerKey.triggerKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
- CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
- //说明本条任务还没有添加到quartz中
- if (null == trigger) {
- if(status.equals("0")){ //如果是禁用,则不用创建触发器
- continue;
- }
- JobDetail jobDetail=null;
- try {
- //创建JobDetail(数据库中job_name存的任务全路径,这里就可以动态的把任务注入到JobDetail中)
- jobDetail = JobBuilder.newJob((Class<? extends Job>) Class.forName(scheduleJob.getJobName()))
- .withIdentity(scheduleJob.getJobName(), scheduleJob.getJobGroup()).build();
- //表达式调度构建器
- CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob
- .getCron());
- //按新的cronExpression表达式构建一个新的trigger
- trigger = TriggerBuilder.newTrigger().withIdentity(scheduleJob.getJobName(), scheduleJob.getJobGroup()).withSchedule(scheduleBuilder).build();
- //把trigger和jobDetail注入到调度器
- scheduler.scheduleJob(jobDetail, trigger);
- } catch (ClassNotFoundException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- } else { //说明查出来的这条任务,已经设置到quartz中了
- // Trigger已存在,先判断是否需要删除,如果不需要,再判定是否时间有变化
- if(status.equals("0")){ //如果是禁用,从quartz中删除这条任务
- JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
- scheduler.deleteJob(jobKey);
- continue;
- }
- String searchCron = scheduleJob.getCron(); //获取数据库的
- String currentCron = trigger.getCronExpression();
- if(!searchCron.equals(currentCron)){ //说明该任务有变化,需要更新quartz中的对应的记录
- //表达式调度构建器
- CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(searchCron);
- //按新的cronExpression表达式重新构建trigger
- trigger = trigger.getTriggerBuilder().withIdentity(triggerKey)
- .withSchedule(scheduleBuilder).build();
- //按新的trigger重新设置job执行
- scheduler.rescheduleJob(triggerKey, trigger);
- }
- }
- }
- }
- } catch (Exception e) {
- logger.error("定时任务每日刷新触发器任务异常,在ScheduleTriggerServiceImpl的方法refreshTrigger中,异常信息:",e);
- }
- }
7、自定义任务
- @Component
- public class MyTask1 implements Job{
- //这里就可以通过spring注入bean了
- @Autowired
- private CScheduleTriggerRepository jobRepository;
- @Autowired
- private CScheduleRecordsRepository recordsRepository;
- @Override
- public void execute(JobExecutionContext context)
- throws JobExecutionException {
- boolean isExecute = false; //是否已执行业务逻辑
- boolean flag = false; //业务逻辑执行后返回结果
- try{
- //可以通过context拿到执行当前任务的quartz中的很多信息,如当前是哪个trigger在执行该任务
- CronTrigger trigger = (CronTrigger) context.getTrigger();
- String corn = trigger.getCronExpression();
- String jobName = trigger.getKey().getName();
- String jobGroup = trigger.getKey().getGroup();
要搞清楚一个问题:从数据库读取任务信息动态生成定时任务,和把quartz持久化到数据库是没有关系的。
前者是我们自己定义的业务表,而后者是quartz使用自己的表来存储信息。持久化到数据库后,就算服务器重启或是多个quartz节点也没关系,因为他们共享数据库中的任务信息。