spring boot1.0 集成quartz 动态配置定时任务

时间:2021-02-08 08:01:25

转载自 https://www.imooc.com/article/36278

一、Quartz简介
了解 Quartz 

Quartz 是一个完全由 Java 编写的开源作业调度框架,为在 Java 应用程序中进行作业调度提供了简单却强大的机制。
Quartz 可以与 J2EE 与 J2SE 应用程序相结合也可以单独使用。
Quartz 允许程序开发人员根据时间的间隔来调度作业。
Quartz 实现了作业和触发器的多对多的关系,还能把多个作业与不同的触发器关联。
Quartz 核心概念

Job 表示一个工作,要执行的具体内容。此接口中只有一个方法,如下:void execute(JobExecutionContext context)
JobDetail 表示一个具体的可执行的调度程序,Job 是这个可执行程调度程序所要执行的内容,另外 JobDetail 还包含了这个任务调度的方案和策略。 
Trigger 代表一个调度参数的配置,什么时候去调。 
Scheduler 代表一个调度容器,一个调度容器中可以注册多个 JobDetail 和 Trigger。当 Trigger 与 JobDetail 组合,就可以被 Scheduler 容器调度了。
集群Quartz应用

伸缩性
高可用性
负载均衡
Quartz可以借助关系数据库和JDBC作业存储支持集群。
Terracotta扩展quartz提供集群功能而不需要数据库支持

 集成quartz,使用quartz,就要理解里面的声明
scheduler:任务调度器
trigger:任务触发器,用于定义任务调度时间规则
job:待执行的任务,即被调度的任务
misfire:没有启动,指本应该被执行但实际没有被执行的任务调度

1.1首先在pom文件加上quartz依赖,将下面maven依赖添加到 dependencies节点里面

<!-- quartz 使用了该jar包PlatformTransactionManager类 -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-tx</artifactId>
</dependency>
<!-- 该依赖里面有spring对schedule的支持 -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context-support</artifactId>
</dependency>
<dependency> 
    <groupId>org.quartz-scheduler</groupId>
    <artifactId>quartz</artifactId>
    <version>2.2.1</version>
</dependency>

2.2 配置定时器
一般spring常用的定时任务框架有:
2.2.1 Spring Schedule 这是spring自带的任务框架 优点就是配置简单,依赖少,缺点就是
同一个task,如果前一个还没跑完后面一个就不会触发,不同的task也不能同时运行。因为scheduler的默认线程数为1,配置pool-size为2的话,会导致同一个task前一个还没跑完后面又被触发的问题,不支持集群等。想了解的同学可以看下面代码

package com.imooc.quartz.task;

import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.util.Date;

/**
 * spring自带任务框架,有弊端
 */
@Component
@EnableScheduling //必须加噢
public class SpringScheduleTask {

    /**
     *  每分钟执行一次
     */
    @Scheduled(cron = "0 0/1 * * * ?")
    public void reptilian(){
        System.out.println("执行调度任务:"+new Date());
    }
}

2.2.2 Quartz 优点太多了,最主要有负载均衡,容错率高等特点,具体可以参考别人的分享
使首先先要创建一个任务调度器,触发器等

MethodInvokingJobDetailFactoryBean:此工厂主要用来制作一个jobDetail,即制作一个任务。由于我们所做的定时任务根本上讲其实就是执行一个方法。所以用这个工厂比较方便。

注意:其setTargetObject所设置的是一个对象而不是一个类。

CronTriggerFactoryBean:定义一个触发器。

注意:setCronExpression:是一个表达式,如果此表达式不合规范,即会抛出异常。

SchedulerFactoryBean:主要的管理的工厂,这是最主要的一个bean。quartz通过这个工厂来进行对各触发器的管理。

 

代码如下

package com.imooc.quartz.config;

import com.imooc.quartz.task.QuartzTask;
import org.quartz.JobDetail;
import org.quartz.Trigger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.quartz.CronTriggerFactoryBean;
import org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;

/**
 * 配置任务
 */
@Configuration
public class QuartzConfiguration {

    /**
     *  配置任务
     * @param quartzTask QuartzTask为需要执行的任务
     * @return
     */
    @Bean(name = "reptilianJob")
    public MethodInvokingJobDetailFactoryBean detailFactoryBean(QuartzTask quartzTask) {

        MethodInvokingJobDetailFactoryBean jobDetail = new MethodInvokingJobDetailFactoryBean();

        // 是否并发执行
        jobDetail.setConcurrent(false);

        // 设置任务的名字
        jobDetail.setName("reptilianJob");

        // 设置任务的分组,在多任务的时候使用
        jobDetail.setGroup("reptilianJobGroup");

        // 需要执行的对象
        jobDetail.setTargetObject(quartzTask);

        /*
         * TODO  非常重要
         * 执行QuartzTask类中的需要执行方法
         */
        jobDetail.setTargetMethod("reptilian");
        return jobDetail;
    }

    /**
     * 定时触发器
     * @param reptilianJob 任务
     * @return
     */
    @Bean(name = "jobTrigger")
    public CronTriggerFactoryBean cronJobTrigger(JobDetail reptilianJob){

        CronTriggerFactoryBean tigger = new CronTriggerFactoryBean();

        tigger.setJobDetail(reptilianJob);

        //cron表达式,每1分钟执行一次
        tigger.setCronExpression("0 0/1 * * * ?");
        tigger.setName("reptilianTrigger");
        return tigger;
    }

    /**
     * 调度工厂
     * @param jobTrigger 触发器
     * @return
     */
    @Bean(name = "scheduler")
    public SchedulerFactoryBean schedulerFactory(Trigger jobTrigger) {

        SchedulerFactoryBean factoryBean = new SchedulerFactoryBean();

        // 用于quartz集群,QuartzScheduler 启动时更新己存在的Job
        factoryBean.setOverwriteExistingJobs(true);

        // 延时启动,应用启动1秒后
        factoryBean.setStartupDelay(1);

        // 注册触发器
        factoryBean.setTriggers(jobTrigger);
        return factoryBean;
    }
}
3创建需要执行的业务逻辑类
package com.imooc.quartz.task;

import org.springframework.stereotype.Service;

import java.util.Date;

/**
 * 使用quartz框架
 */
@Service
public class QuartzTask {

    /**
     * 业务逻辑
     */
    public void reptilian(){
        System.out.println("执行业务处理逻辑:"+new Date());
    }
}

 4定时查询数据库,并更新任务

package com.example.demo.service;
 
import com.example.demo.mapper.ConfigMapper;
import lombok.extern.slf4j.Slf4j;
import org.quartz.*;
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 javax.annotation.Resource;
 
/**
 * 路径:com.example.demo.config
 * 类名:
 * 功能:定时产查询数据库,并更新任务
 * 备注:
 * 创建人:typ
 * 创建时间:2018/10/10 14:15
 * 修改人: 
 * 修改备注:
 * 修改时间:
 */
@Slf4j
@Configuration
@EnableScheduling
@Component
public class ScheduleRefreshService {
 
    @Autowired
    private ConfigMapper configMapper;
 
    @Resource(name = "jobDetail")
    private JobDetail jobDetail;
 
    @Resource(name = "jobTrigger")
    private CronTrigger cronTrigger;
 
    @Resource(name = "scheduler")
    private Scheduler scheduler;
 
    /**
     * 方法名:
     * 功能:每隔10s查库,并根据查询结果决定是否重新设置定时任务
     * 描述:
     * 创建人:typ
     * 创建时间:2018/10/10 14:19
     * 修改人:
     * 修改描述:
     * 修改时间:
     */
    @Scheduled(fixedRate = 10000)
    public void scheduleUpdateCronTrigger() throws SchedulerException {
        CronTrigger trigger = (CronTrigger) scheduler.getTrigger(cronTrigger.getKey());
        //当前Trigger使用的
        String currentCron = trigger.getCronExpression();
        log.info("currentCron Trigger:{}", currentCron);
        //从数据库查询出来的
        String searchCron = configMapper.findOne(1).getCron();
        log.info("searchCron  Trigger:{}", searchCron);
 
        if (currentCron.equals(searchCron)) {
            // 如果当前使用的cron表达式和从数据库中查询出来的cron表达式一致,则不刷新任务
        } else {
            //表达式调度构建器
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(searchCron);
            //按新的cronExpression表达式重新构建trigger
            trigger = (CronTrigger) scheduler.getTrigger(cronTrigger.getKey());
            trigger = trigger.getTriggerBuilder().withIdentity(cronTrigger.getKey()).withSchedule(scheduleBuilder).build();
            // 按新的trigger重新设置job执行
            scheduler.rescheduleJob(cronTrigger.getKey(), trigger);
            currentCron = searchCron;
        }
    }
}