基于springBoot动态配置定时任务

引用地址:https://blog.csdn.net/qq_14861089/article/details/108831101
功能实现:
1.实体类:

package com.mengan.utilsProject.task;

import lombok.Data;

@Data
public class Scheduled {


    private int id;

    private String taskKey;

    private String name;

    private String cron;

    private int status;

    private String createTime;

    private String updateTime;
}

2.Mapper接口类:

package com.mengan.utilsProject.task;

import org.apache.ibatis.annotations.Mapper;

import java.util.List;

@Mapper
//@Repository
public interface ScheduledMapper {

    List<Scheduled> selectList(Scheduled scheduled);
}

3.Mapper接口对应的xml文件:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.mengan.utilsProject.task.ScheduledMapper">

    <select id="selectList" resultType="com.mengan.utilsProject.task.Scheduled">
        select * from scheduled where status = 1
        <if test="taskKey != null and taskKey != ''">
            AND task_key = #{taskKey}
        </if>
    </select>

</mapper>

4.状态枚举类

package com.mengan.utilsProject.task;


public enum ScheduledStatus {

    ENABLE(0, "禁用"),
    DISABLE(1, "启用");

    private int code;
    private String name;

    ScheduledStatus(int code, String name){
        this.code = code;
        this.name = name;
    }

    public static ScheduledStatus getByCode(int code){
        for (ScheduledStatus st : values()) {
            if(code == st.getCode())
                return st;
        }
        return null;
    }

    public int getCode() {
        return code;
    }

    public String getName() {
        return name;
    }

}

5.配置类

package com.mengan.utilsProject.task;


import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;

@Slf4j
@Configuration
public class ScheduledTaskConfig {

    @Bean
    public ThreadPoolTaskScheduler threadPoolTaskScheduler() {
        log.info("创建定时任务调度线程池 start");
        ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
        threadPoolTaskScheduler.setPoolSize(20);
        threadPoolTaskScheduler.setThreadNamePrefix("taskExecutor-");
        //用来设置线程池关闭的时候等待所有任务都完成再继续销毁其他的Bean,这样这些异步任务的销毁就会先于Redis线程池的销毁。
        threadPoolTaskScheduler.setWaitForTasksToCompleteOnShutdown(true);
        //该方法用来设置线程池中任务的等待时间,如果超过这个时候还没有销毁就强制销毁,以确保应用最后能够被关闭,而不是阻塞住。
        threadPoolTaskScheduler.setAwaitTerminationSeconds(60);
        log.info("创建定时任务调度线程池 end");
        return threadPoolTaskScheduler;
    }

}

6.自定义定时任务接口

package com.mengan.utilsProject.task;


/**
 * @ClassName ScheduledTaskJob
 * @Description 创建调度任务公共父接口
 * @Author 
 * @Date
 * @Version
 */
public interface ScheduledTaskJob extends Runnable{
}

7.自定义定时任务实现类1

package com.mengan.utilsProject.task;


import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

/**
 * @ClassName ScheduledTaskJob01
 * @Description 测试类01
 * @Author
 * @Date 2022/01/26
 * @Version 1.0
 */
@Slf4j
@Service
public class ScheduledTaskJob01 implements ScheduledTaskJob {

    @Override
    public void run() {
        // TODO 要处理的业务逻辑
        log.info("ScheduledTask => 01  run  当前线程名称 {} ", Thread.currentThread().getName());
    }
}

8.自定义定时任务实现类2

package com.mengan.utilsProject.task;


import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

/**
 * @ClassName ScheduledTaskJob02
 * @Description 测试类02
 * @Author
 * @Date 2022/01/26
 * @Version 1.0
 */
@Slf4j
@Service
public class ScheduledTaskJob02 implements ScheduledTaskJob {

    @Override
    public void run() {
        // TODO 要处理的业务逻辑
        log.info("ScheduledTask => 02  run  当前线程名称 {} ", Thread.currentThread().getName());
    }
}

9.自定义定时任务实现类3

package com.mengan.utilsProject.task;


import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

/**
 * @ClassName ScheduledTaskJob03
 * @Description 测试类03
 * @Author
 * @Date 2022/01/26
 * @Version 1.0
 */
@Slf4j
@Service
public class ScheduledTaskJob03 implements ScheduledTaskJob {

    @Override
    public void run() {
        // TODO 要处理的业务逻辑
        log.info("ScheduledTask => 03  run  当前线程名称 {} ", Thread.currentThread().getName());
    }
}

10.ScheduledTaskRunner类:

package com.mengan.utilsProject.task;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;

@Slf4j
@Component
public class ScheduledTaskRunner implements ApplicationRunner {

    @Autowired
    private ScheduledTaskService scheduledTaskService;

    /**
     * 程序启动完毕后,需要自启的任务
     */
    @Override
    public void run(ApplicationArguments applicationArguments){
        log.info(" >>>>>> 项目启动完毕, 开启 => 需要自启的任务 开始!");
        scheduledTaskService.initAllTask();
        log.info(" >>>>>> 项目启动完毕, 开启 => 需要自启的任务 结束!");
    }

}

11.ScheduledTaskService接口

package com.mengan.utilsProject.task;



/**
 * @ClassName ScheduledTaskService
 * @Description 定时任务接口
 * @Author
 * @Date
 * @Version 1.0
 */
public interface ScheduledTaskService {

    /**
     * 根据任务key 启动任务
     */
    Boolean start(String taskKey, Scheduled scheduled);

    /**
     * 根据任务key 停止任务
     */
    Boolean stop(String taskKey);

    /**
     * 根据任务key 重启任务
     */
    Boolean restart(String taskKey, Scheduled scheduled);

    /**
     * 初始化  ==> 启动所有正常状态的任务
     */
    void initAllTask();
}

12.定时任务实现类

package com.mengan.utilsProject.task;


import cn.hutool.extra.spring.SpringUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.TriggerContext;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @ClassName ScheduledTaskServiceImpl
 * @Description 定时任务实现
 * @Author
 * @Date
 * @Version 1.0
 */
@Slf4j
@Service
public class ScheduledTaskServiceImpl implements ScheduledTaskService {

    @Value("${task.enabled}")
    private Boolean taskEnable;

    /**
     * 可重入锁
     */
    private ReentrantLock lock = new ReentrantLock();
    /**
     * 定时任务线程池
     */
    @Autowired
    private ThreadPoolTaskScheduler threadPoolTaskScheduler;

    /**
     * 存放已经启动的任务map
     */
    private Map<String, ScheduledFuture> scheduledFutureMap = new ConcurrentHashMap<>();

    @Autowired
    private ScheduledMapper scheduledMapper;

    /**
     * 描述: 根据任务key 启动任务
     *
     * @param taskKey
     * @param scheduled
     * @return java.lang.Boolean
     * @author lv617
     * @date 2020/9/24 11:16
     */
    @Override
    public Boolean start(String taskKey, Scheduled scheduled) {
        log.info(">>>>>> 启动任务 {} 开始 >>>>>>", taskKey);
        //添加锁放一个线程启动,防止多人启动多次
        lock.lock();
        log.info(">>>>>> 添加任务启动锁完毕");
        try {
            //校验是否已经启动
            if (this.isStart(taskKey)) {
                log.info(">>>>>> 当前任务已经启动,无需重复启动!");
                return false;
            }
            //查询配置
            if(scheduled == null)
                scheduled = this.getByTaskKey(taskKey);
            if(scheduled == null)
                return false;
            //启动任务
            this.doStartTask(scheduled);
        } finally {
            // 释放锁
            lock.unlock();
            log.info(">>>>>> 释放任务启动锁完毕");
        }
        log.info(">>>>>> 启动任务 {} 结束 >>>>>>", taskKey);
        return true;
    }

    /**
     * 描述: 查询定时任务配置参数
     *
     * @param taskKey
     * @return com.yihaocard.main.module.scheduled.model.Scheduled
     * @author lv617
     * @date 2020/9/24 11:14
     */
    private Scheduled getByTaskKey(String taskKey) {
        Scheduled scheduledExample = new Scheduled();
        scheduledExample.setStatus(1);
        scheduledExample.setTaskKey(taskKey);
        List<Scheduled> scheduleds = scheduledMapper.selectList(scheduledExample);
        if(scheduleds == null || scheduleds.size() < 1)
            return null;
        return scheduleds.get(0);
    }

    /**
     * 描述: 根据 key 停止任务
     *
     * @param taskKey
     * @return java.lang.Boolean
     * @author lv617
     * @date 2020/9/24 11:17
     */
    @Override
    public Boolean stop(String taskKey) {
        log.info(">>>>>> 进入停止任务 {}  >>>>>>", taskKey);
        //当前任务实例是否存在
        boolean taskStartFlag = scheduledFutureMap.containsKey(taskKey);
        log.info(">>>>>> 当前任务实例是否存在 {}", taskStartFlag);
        if (taskStartFlag) {
            //获取任务实例
            ScheduledFuture scheduledFuture = scheduledFutureMap.get(taskKey);
            //关闭实例
            boolean cancel = scheduledFuture.cancel(true);
            log.info("cancel:{}", cancel);
            //删除关闭的任务实例
            scheduledFutureMap.remove(taskKey);
        }
        log.info(">>>>>> 结束停止任务 {}  >>>>>>", taskKey);
        return taskStartFlag;
    }

    /**
     * 描述: 根据任务key 重启任务
     *
     * @param taskKey
     * @param scheduled
     * @return java.lang.Boolean
     * @author lv617
     * @date 2020/9/24 11:18
     */
    @Override
    public Boolean restart(String taskKey, Scheduled scheduled) {
        log.info(">>>>>> 进入重启任务 {}  >>>>>>", taskKey);
        //先停止
        this.stop(taskKey);
        //查询配置
        if(scheduled == null)
            scheduled = this.getByTaskKey(taskKey);
        if(scheduled == null)
            return false;
        //再启动
        return this.start(taskKey,scheduled);
    }

    /**
     * 初始化  ==> 启动所有正常状态的任务
     */
    @Override
    public void initAllTask() {
        if(!taskEnable){
            log.info("配置文件禁用了定时任务----");
            return;
        }
        Scheduled scheduledExample = new Scheduled();
        scheduledExample.setStatus(1);
        List<Scheduled> scheduleds = scheduledMapper.selectList(scheduledExample);
        log.info("初始化  ==> 启动所有正常状态的任务开始 !size={}", scheduleds == null ? 0 : scheduleds.size());
        if (scheduleds == null || scheduleds.size() < 1) {
            return;
        }
        for (Scheduled scheduled : scheduleds) {
            //任务 key
            String taskKey = scheduled.getTaskKey();
            //校验是否已经启动
            if (this.isStart(taskKey)) {
                // 重启任务
                this.restart(taskKey,scheduled);
            } else {
                // 启动任务
                this.doStartTask(scheduled);
            }
        }
        log.info("初始化  ==> 启动所有正常状态的任务结束 !");
    }

    /**
     * 执行启动任务
     */
    private void doStartTask(Scheduled scheduled) {
        if (scheduled == null)
            return;
        //任务key
        String taskKey = scheduled.getTaskKey();
        //定时表达式
        String taskCron = scheduled.getCron();
        //获取需要定时调度的接口
        ScheduledTaskJob scheduledTaskJob = (ScheduledTaskJob) SpringUtil.getBean(taskKey);
        log.info(">>>>>> 任务 [ {} ] ,cron={}", scheduled.getName(), taskCron);
        ScheduledFuture scheduledFuture = threadPoolTaskScheduler.schedule(scheduledTaskJob, (TriggerContext triggerContext) -> new CronTrigger(taskCron).nextExecutionTime(triggerContext));
        //将启动的任务放入 map
        scheduledFutureMap.put(taskKey, scheduledFuture);
    }

    /**
     * 任务是否已经启动
     */
    private Boolean isStart(String taskKey) {
        //校验是否已经启动
        if (scheduledFutureMap.containsKey(taskKey)) {
            if (!scheduledFutureMap.get(taskKey).isCancelled()) {
                return true;
            }
        }
        return false;
    }

}

13.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.mengan.utilsProject.task.ScheduledMapper">

    <select id="selectList" resultType="com.mengan.utilsProject.task.Scheduled">
        select * from scheduled where status = 1
        <if test="taskKey != null and taskKey != ''">
            AND task_key = #{taskKey}
        </if>
    </select>

</mapper>

14.application.yml文件

server:
  port: 38080

task:
  enabled: 1

spring:
  datasource:
    username: root
    password: root
    url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC
    driver-class-name: com.mysql.jdbc.Driver

mybatis:
  mapper-locations: classpath:mapping/*Mapper.xml
  type-aliases-package: com.mengan.utilsProject.task
  configuration:
    map-underscore-to-camel-case: true
#showSql
logging:
  level:
    com:
      example:
        mapper : debug

15.数据库表:

CREATE TABLE `scheduled` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `task_key` varchar(127) NOT NULL COMMENT '任务key值(使用bean名称)',
  `name` varchar(127) DEFAULT NULL COMMENT '任务名称',
  `cron` varchar(63) NOT NULL COMMENT '任务表达式',
  `status` int(2) DEFAULT '0' COMMENT '状态(0.禁用; 1.启用)',
  `create_time` timestamp NULL DEFAULT NULL COMMENT '创建时间',
  `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uniqu_task_key` (`task_key`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='定时任务配置表';

在这里插入图片描述
16.执行结果:
在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值