springBoot整合quartz数据库管理定时任务样例demo

样例介绍

采用quartz,并使用数据库来动态管理定时任务。

步骤

(1)导入依赖



    org.quartz-scheduler
    quartz
    2.3.0


    org.springframework
    spring-context-support



    mysql
    mysql-connector-java
    runtime


    com.baomidou
    mybatis-plus-boot-starter
    3.0.5



    com.alibaba
    druid-spring-boot-starter
    1.1.10

(2)添加配置

数据库配置:

spring.datasource.druid.url=jdbc:mysql://*.*.*.*(这里使用自己服务器的ip):3306/(自己的数据库)?reWriteBatchedInserts=true
spring.datasource.druid.username=root
spring.datasource.druid.password=123456
spring.datasource.druid.driver-class-name=com.mysql.jdbc.Driver

quartz配置:

#--------------------------------------------------------------
#调度器属性
#--------------------------------------------------------------
# 调度器实例名:可以随意命名
org.quartz.scheduler.instanceName=quartz-demo
# 实例ID: 允许随意命名,但必须保持唯一,集群可以让quartz来帮你命名,设置为AUTO
org.quartz.scheduler.instanceId=AUTO
#只有org.quartz.scheduler.instanceId设置为“AUTO”才使用。
# 默认为“org.quartz.simpl.SimpleInstanceIdGenerator”,它是主机名和时间戳生成实例Id的。
# 其它的IntanceIdGenerator实现包括SystemPropertyInstanceIdGenerator(它从系统属性“org.quartz.scheduler.instanceId”获取实例Id),
# 和HostnameInstanceIdGenerator(它使用本地主机名InetAddress.getLocalHost().getHostName()生成实例Id)。
# 你也实现你自己的InstanceIdGenerator
org.quartz.scheduler.instanceIdGenerator.class=com.yw.quartzdemo.support.QuartzInstanceIdGenerator
#--------------------------------------------------------------
# 线程池属性
#--------------------------------------------------------------
#  org.quartz.threadPool.class这个值是一个实现了 org.quartz.spi.ThreadPool 接口的类的全限名称。
#  Quartz 自带的线程池实现类是 org.quartz.smpl.SimpleThreadPool,
#  它能够满足大多数用户的需求。这个线程池实现具备简单的行为,并经很好的测试过。
#  它在调度器的生命周期中提供固定大小的线程池。
#  你能根据需求创建自己的线程池实现,如果你想要一个随需可伸缩的线程池时也许需要这么做。
#  这个属性没有默认值,你必须为其指定值。
org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool
# threadCount 属性控制了多少个工作者线程被创建用来处理 Job。
# 原则上是,要处理的 Job 越多,那么需要的工作者线程也就越多。
# threadCount 的数值至少为 1。
# Quartz 没有限定你设置工作者线程的最大值,但是在多数机器上设置该值超过100的话就会显得相当不实用了,
# 特别是在你的 Job 执行时间较长的情况下。
# 这项没有默认值,所以你必须为这个属性设定一个值
org.quartz.threadPool.threadCount=10
# 优先级:可以是Thread.MIN_PRIORITY (1)和Thread.MAX_PRIORITY (10)之间的任意整数。默认为Thread.NORM_PRIORITY (5).
org.quartz.threadPool.threadPriority=5
# 继承初始化线程的上下文类加载器线程
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread=true
#--------------------------------------------------------------
#作业存储设置:作业存储部分的设置描述了在调度器实例的生命周期中,Job 和 Trigger 信息是如何被存储的。
# 把调度器信息存储在内存中非常的快也易于配置。
# 当调度器进程一旦被终止,所有的 Job 和 Trigger 的状态就丢失了。
# 要使 Job 存储在内存中需通过设置 org.quartz.jobStrore.class 属性为 org.quartz.simpl.RAMJobStore,
# 在Cron Trigger 和“作业存储和持久化”会用到的不同类型的作业存储实现。
#--------------------------------------------------------------
# trigger被认为失败之前,scheduler能够承受的下一次触发时间(单位毫秒)。默认值为60秒。
org.quartz.jobStore.misfireThreshold=60000
# 用于将调度信息(job、trigger和calendar)存储到关系数据库中。
org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX
# 设置当前数据库Driver代理的数据库系统的方言
org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
#org.quartz.jobStore.driverDelegateClass:org.quartz.impl.jdbcjobstore.PostgreSQLDelegate
# 表前缀
org.quartz.jobStore.tablePrefix=QRTZ_
# JobStore处理失败trigger的最大等待时间。
# 同时处理多个trigger(多于几个)回引发数据表长时间锁定,触发其它的trigger(还没有失败)的性能就会受到限制
org.quartz.jobStore.maxMisfiresToHandleAtATime=10
# 使用集群特性,这个属性必须为true。
# 如果你有多个Quartz实例使用相同的数据库表,这个属性必须为true,否则你会体验一把大破坏。参见集群配置。
org.quartz.jobStore.isClustered=true  
# 设置当前实例check in集群中其它实例的频率。影响检测到故障实例的速度
org.quartz.jobStore.clusterCheckinInterval=20000
# “org.quartz.jobStore.useProperties”配置参数可以被设置为true(默认为false),
# 这样可以指导JDBCJobStore,JobDataMaps中的值都是字符串,因此这样可以以名字-值对存储,
# 而不是存储更加复杂的对象(序列化形式BLOB)。
# 从长远来看,这是很安全的,因为避免了将非字符串类序列化为BLOB的类版本问题
org.quartz.jobStore.useProperties=true

(3)添加定时任务表的PO,mapper,dao

po:

import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;


@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("JOB_DEF")
@Builder
public class JobDefPO {
    /**
     * 任务名称-类全名
     */
    private String jobName;

    /**
     * 任务分组
     */
    private String jobGroup;

    /**
     * 任务执行表达式
     */
    private String cron;

    /**
     * 任务状态
     */
    private Integer status;

}

mapper:

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.yw.quartzdemo.po.JobDefPO;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface JobDefMapper extends BaseMapper {

}

dao

import com.yw.quartzdemo.po.JobDefPO;

import java.util.List;

/**
 * 任务定义接口
 */
public interface JobDefDAO {

    /***
     * @Description 查询所有任务
     * @author yuanwei
     * @param 
     * @return java.util.List
     * @time 2021/1/6 16:35
     */
    List listAll();
}

dao.impl:

import com.baomidou.mybatisplus.core.conditions.Condition;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yw.quartzdemo.dao.JobDefDAO;
import com.yw.quartzdemo.mapper.JobDefMapper;
import com.yw.quartzdemo.po.JobDefPO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * 任务定义实现
 */
@Service
@Slf4j
public class JobDefDAOImpl extends ServiceImpl implements JobDefDAO {

    
    @Override
    public List listAll() {
        List result = null;
        final QueryWrapper wrapper = Condition.create();
        try {
            result = this.list(wrapper);
        } catch (final Exception e) {
            log.error("查询定时任务定义异常:{},{}", e.getMessage(), e);
            throw new RuntimeException("查询定时任务定义失败");
        }
        return result;
    }
}

(4)添加配置:

config包下

quartzConfig.java

import lombok.extern.slf4j.Slf4j;
import org.quartz.Scheduler;
import org.quartz.spi.TriggerFiredBundle;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
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.AdaptableJobFactory;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;

import javax.sql.DataSource;
import java.io.IOException;
import java.util.Properties;

@Configuration
@Slf4j
public class QuartzConfig {

    @Autowired
    private AutowireCapableBeanFactory capableBeanFactory;

    @Autowired
    private DataSource dataSource;

    public Properties quartzProperties() throws IOException {

        final PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
        propertiesFactoryBean.setLocation(new ClassPathResource("quartz.properties"));
        propertiesFactoryBean.afterPropertiesSet();
        return propertiesFactoryBean.getObject();
    }

    @Bean(name = "quartzJobFactory")
    public AdaptableJobFactory quartzJobFactory() {
        return new AdaptableJobFactory() {
            @Override
            protected Object createJobInstance(final TriggerFiredBundle bundle) throws Exception {

                final Object jobInstance = super.createJobInstance(bundle);
                capableBeanFactory.autowireBean(jobInstance);
                return jobInstance;
            }
        };
    }

    @Bean(name = "schedulerFactoryBean")
    public SchedulerFactoryBean schedulerFactoryBean(@Qualifier("quartzJobFactory") final AdaptableJobFactory quartzJobFactory) {

        final SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
        try {
            schedulerFactoryBean.setQuartzProperties(quartzProperties());
            schedulerFactoryBean.setDataSource(dataSource);
            schedulerFactoryBean.setJobFactory(quartzJobFactory);
            schedulerFactoryBean.setStartupDelay(5);
            schedulerFactoryBean.setOverwriteExistingJobs(true);
        } catch (final IOException ex) {
            log.error(ex.getMessage(), ex);
            throw new RuntimeException("定时任务调度实体构建异常");
        }
        return schedulerFactoryBean;
    }

    @Bean(name = "scheduler")
    public Scheduler scheduler(@Qualifier("schedulerFactoryBean") final SchedulerFactoryBean schedulerFactoryBean) {

        return schedulerFactoryBean.getScheduler();
    }
}

supper包下:

JobRefresh.java 用于设置定时刷新频率。

import com.yw.quartzdemo.dao.JobDefDAO;
import com.yw.quartzdemo.po.JobDefPO;
import lombok.extern.slf4j.Slf4j;
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.SchedulerException;
import org.quartz.TriggerBuilder;
import org.quartz.TriggerKey;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.List;

/**
 * @description
 * @author yuanwei
 * @date 2021/1/6 11:17
 */
@Component
@Slf4j
public class JobRefresh {

    @Autowired
    private JobDefDAO jobDefDAO;

    @Autowired
    private Scheduler scheduler;

    @SuppressWarnings("unchecked")
    @Scheduled(cron = "0 0 1 * * ?") // 每天凌晨1点刷新一次
    @PostConstruct // 应用启动时刷新一次,方便测试,后续可以去掉
    public void refreshTrigger() throws SchedulerException {

        final List jobDefs = jobDefDAO.listAll();

        for (final JobDefPO jobDef : jobDefs) {
            final TriggerKey triggerKey = TriggerKey.triggerKey(jobDef.getJobName(), jobDef.getJobGroup());
            CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
            if (null == trigger) {
                // 状态(0:正常,1:禁用)
                if (jobDef.getStatus().intValue() == 1) {
                    continue;
                }
                JobDetail jobDetail = null;
                try {
                    jobDetail = JobBuilder.newJob((Class) Class.forName(jobDef.getJobName()))
                            .withIdentity(jobDef.getJobName(), jobDef.getJobGroup()).build();
                } catch (final ClassNotFoundException ex) {
                    log.error(ex.getMessage(), ex);
                    throw new RuntimeException("刷新任务异常");
                }
                final CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(jobDef.getCron());
                trigger = TriggerBuilder.newTrigger().withIdentity(jobDef.getJobName(), jobDef.getJobGroup())
                        .withSchedule(scheduleBuilder).build();
                scheduler.scheduleJob(jobDetail, trigger);

            } else {
                if (jobDef.getStatus().intValue() == 1) {
                    final JobKey jobKey = JobKey.jobKey(jobDef.getJobName(), jobDef.getJobGroup());
                    scheduler.deleteJob(jobKey);
                    continue;
                }
                final String selectedCron = jobDef.getCron();
                final String currentCron = trigger.getCronExpression();
                if (!selectedCron.equals(currentCron)) {
                    final CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(selectedCron);
                    trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder)
                            .build();
                    scheduler.rescheduleJob(triggerKey, trigger);
                }
            }
        }

    }
}


(5)添加定时任务

import lombok.extern.slf4j.Slf4j;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

import java.util.Date;

/**
 * @description
 * @author yuanwei
 * @date 2021/1/6 16:38
 */
@Slf4j
public class DemoJobHandler implements Job {

    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        Date time = new Date();
        log.info("时间:{},打印",time);
    }
}


(6)在数据库中添加(5)中任务的触发时间

其中status中0为开启,1为关闭。

INSERT INTO `fund`.`JOB_DEF`(`JOB_NAME`, `JOB_GROUP`, `CRON`, `STATUS`) VALUES ('com.yw.quartzdemo.handler.DemoJobHandler', 'demo', '* * * * * ?', 0);

github链接:https://github.com/source201/yw-doc

你可能感兴趣的:(springBoot整合quartz数据库管理定时任务样例demo)