关于SpringBoot动态定时任务的实现

2022-07-28,,

简单介绍

  1. 通过Spring自己的任务调度包Task实现定时任务的周期动态设置,以及随时启动停止等操作
  2. 需要注意的是,只能单机使用,集群和分布式结构不要考虑,否则会多次执行。可以考虑任务调度框架Quartz,听说很好用, 我还没用过 - -

怎么用

  1. 首先搞个存储设置信息的表
CREATE TABLE `sys_timed_task` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `task_sort` int(11) DEFAULT NULL COMMENT '定时任务序号 (两位一组 例:1020)',
  `task_path` varchar(100) DEFAULT NULL COMMENT '定时任务执行类路径',
  `task_cron` varchar(50) DEFAULT NULL COMMENT '执行周期(cron表达式)',
  `is_del` int(2) DEFAULT '10' COMMENT '是否可删除或停用 10|否/20|是 (立刻生效)',
  `creater_name` varchar(50) DEFAULT NULL COMMENT '创建人',
  `create_time` varchar(20) DEFAULT NULL COMMENT '创建时间',
  `enabled` int(2) DEFAULT '10' COMMENT '是否可用 10|可用/20|停用 (立刻生效)',
  `remark` varchar(100) DEFAULT NULL COMMENT '备注-预留',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COMMENT='定时任务配置表';
  1. 定时配置类
package com.runlin.wework.config;

import com.runlin.wework.entity.SysTimedTaskEntity;
import com.runlin.wework.task.dao.SysTimedTaskDao;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.scheduling.config.Task;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.util.Assert;

import javax.annotation.Resource;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

/**
 * Description:
 * 动态定时任务配置类
 * <p>
 * ClassName: TimedTaskConfig
 * date: 2020/10/22 13:16
 *
 * @author jo.li
 * @version 1.0
 * @since JDK 1.8
 */
@Configuration
public class TimedTaskConfig implements SchedulingConfigurer {
    @Resource
    private ApplicationContext context;
    @Resource
    private SysTimedTaskDao timedTaskDao;


    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
    	// 这里也可以不用selectAll(),自己写个条件查询提前把禁用或已删除数据提出也可以
        for(SysTimedTaskEntity timedTaskEntity : timedTaskDao.selectAll()){
            Class<?> clazz;
            Object task;
            try {
            	// 根据类路径获取实例
                clazz = Class.forName(timedTaskEntity.getTaskPath());
                // 生成bean(放spring里)
                task = context.getBean(clazz);
            } catch (ClassNotFoundException e) {
                throw new IllegalArgumentException("sys_timed_task表数据" + timedTaskEntity.getTaskPath() + "有误", e);
            } catch (BeansException e) {
                throw new IllegalArgumentException(timedTaskEntity.getTaskPath() + "不是SpringBean", e);
            }
            Assert.isAssignable(ScheduledOfTask.class, task.getClass(), "定时任务类没有实现ScheduledOfTask接口");
            // 可以通过改变数据库数据进而实现动态改变执行周期
            taskRegistrar.addTriggerTask(((Runnable) task),
                    triggerContext -> {
                        String cronExpression = timedTaskEntity.getTaskCron();
                        return new CronTrigger(cronExpression).nextExecutionTime(triggerContext);
                    }
            );
        }
    }
	/**
     * 搞个池子给Spring,初始的定时放这里执行 
     * @return: java.util.concurrent.Executor
     * @Author jo.li
     * @Description taskExecutor <br/>
     * @Date 2020/11/11 17:30
     **/
    @Bean
    public Executor taskExecutor() {
    	// 核心线程数根据定时数量和执行效率来
        return new ScheduledThreadPoolExecutor(10, new ThreadFactory() {
            int counter = 0;
            @Override
            public Thread newThread(Runnable r) {
                Thread thread = new Thread(r,"数据统计-Thread-"+counter);
                counter++;
                return thread;
            }
        });
    }
}
  1. 定时实现接口
package com.runlin.wework.config;

import com.runlin.wework.common.GlobalConstant;
import com.runlin.wework.entity.SysTimedTaskEntity;
import com.runlin.wework.task.dao.SysTimedTaskDao;
import com.runlin.wework.utils.SpringUtils;

/**
 * Description:
 * <p>
 * ClassName: ScheduledOfTask
 * date: 2020/10/22 13:23
 *
 * @author jo.li
 * @version 1.0
 * @since JDK 1.8
 */
public interface ScheduledOfTask extends Runnable {
    /**
     * 定时任务业务实现方法
     *
     * @return: void
     * @Author jo.li
     * @Description execute <br/>
     * @Date 2020/10/22 14:43
     **/
    void execute();

    /**
     * 实现控制定时任务启用或禁用的功能
     *
     * @return: void
     * @Author jo.li
     * @Description run <br/>
     * @Date 2020/10/22 14:12
     **/
    @Override
    default void run() {
        SysTimedTaskDao timedTaskDao = SpringUtils.getBean(SysTimedTaskDao.class);
        SysTimedTaskEntity timedTaskEntity = timedTaskDao.getTimedTaskByPath(this.getClass().getName());
        // 禁用或已删除
        if (GlobalConstant.NO_ENABLED.equals(timedTaskEntity.getEnabled()) ||
                GlobalConstant.IS_DEL.equals(timedTaskEntity.getIsDel())) {
            return;
        }
        // 执行
        execute();
    }
}
  1. 定时任务类使用执行类添加注解@Component 实现接口ScheduledOfTask就OK了
package com.runlin.wework.task.data;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * Description:
 * 各类问题咨询人数统计
 * <p>
 * ClassName: QuestionPerDayStatistics
 * date: 2020/10/23 14:29
 *
 * @author jo.li
 * @version 1.0
 * @since JDK 1.8
 */
@Component
public class QuestionPerDayStatistics implements ScheduledOfTask {
    @Resource
    private DataStatisticsUtils statisticsUtils;
    private final Logger log = LoggerFactory.getLogger(getClass());
    /**
     * 统计初始值
     */
    private final String INIT_NUM = "1";

    /**
     * 定时任务业务实现方法
     *
     * @return: void
     * @Author jo.li
     * @Description execute <br/>
     * @Date 2020/10/22 14:43
     **/
    @Override
    public void execute() {
        log.info("各类问题咨询人数统计定时开始运行!");
        
        log.info("各类问题咨询人数统计定时运行结束!");
    }
}
  1. 页面启用停用有时间再说。

本文地址:https://blog.csdn.net/qq_38402364/article/details/109626848

《关于SpringBoot动态定时任务的实现.doc》

下载本文的Word格式文档,以方便收藏与打印。