SpringBoot动态配置定时任务

352 阅读3分钟

准备工作

动态配置定时任务,使用SpringBoot内置的就行,在搭建好SpringBoot框架后不需要再引入其他依赖

所涉及到的类

(1) ScheduleModel类: 动态任务数据库所映射的实体类,动态更改cron表达式

(2) ScheduleConfig类: 定时任务的配置类,需要实现 SchedulingConfigurer接口

(3) ScheduledTask类 : 定时任务,所执行方法类,实现Runable接口

(4) SpringScheduledCronRepository类: 相当于一个工具类,用来添加删除修改定时任务

一、编写ScheduleModel类

先创建数据库表, 以及映射的实体类,下面我只展示实体类的字段,数据库请自建

public class ScheduledModel {

    /**
     * 主键
     */
    private Integer id;

    /**
     * 定时任务的cron表达式
     */
    private String cronExpression;

    /**
     * 定时任务的状态(主要是判断当前定时任务是否被弃用,作为取消的依据)
     */
    private Integer state;
    
    }
CREATE TABLE schedule_model(
id int PRIMARY KEY auto_increment COMMENT '主键',
cron_expression VARCHAR(50) NOT NULL COMMENT 'cron表达式',
state int NOT NULL DEFAULT 1 COMMENT '状态'
)

二、编写ScheduledTask类

这里先编写定时任务执行方法类,这里我就没写过多东西,如果需要传参,需要查数据库,自己构造就好了,根据自己的业务需求来


public class ScheduledTask implements Runnable {
    @Override
    public void run() {
        System.out.println("我是定时任务哦" + new Date());
    }
}

三、编写SpringScheduledCronRepository类

这个类相当于一个工具类或者说是service类,主要是定义了动态任务初始化,增加,取消,查询方法

import com.laohe.entity.SpringScheduledCron;
import lombok.SneakyThrows;
import org.springframework.beans.factory.DisposableBean;
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.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledFuture;

@Component
@Configuration
public class SpringScheduledCronRepository   {

    /**
     * 定时任务的线程池
     */
    @Autowired
    private ThreadPoolTaskScheduler threadPoolTaskScheduler;

    /**
     * ScheduleModel实体类的Mapper
     */
    @Autowired
    private ScheduledModelMapper scheduledModelMapper;

    /**
     * 存放所以定时任务实体类的Map
     */
    private static Map<Integer, ScheduledModel> scheduledModelMap = new ConcurrentHashMap<>();

    /**
     * 存放定时任务的Map
     */
    private static Map<Integer, ScheduledFuture<?>> scheduledFutureMap = new ConcurrentHashMap<>();


    @Bean
    public ThreadPoolTaskScheduler threadPoolTaskScheduler() {
        return new ThreadPoolTaskScheduler();
    }



    /**
     * 初始化任务
     */
    public void initScheduledTask() {
        //1.查询数据库中所有的定时任务
        List<ScheduledModel> scheduledModels = scheduledModelMapper.selectAll();
        scheduledModels.forEach(scheduledModel -> {
            addTask(scheduledModel);
        });
    }



  
    /**
     * 增加任务
     */
    public void addTask(ScheduledModel scheduledModel) {
        //1.先判定该定时任务的状态
        if (scheduledModel.getState() == 0) {
            return;
        }
        //2.增加定时任务,map中是否存在了如果存在 移除取消
        if (scheduledModelMap.containsKey(scheduledModel.getId())) {
            cancelTask(scheduledModel.getId());
        }
        // 定时任务线程方法可以传入参数 根据自己需求去定义吧
        // 如果定时任务线程方法也想动态更新  这里就不使用new的方式去创建对象
        // 使用 ApplicationContext去获取
        ScheduledTask task = new ScheduledTask();

        //3.添加定时任务
        ScheduledFuture<?> schedule = threadPoolTaskScheduler.schedule(task, triggerContext -> {
            // 通过当前ID查询到定时任务信息
            ScheduledModel scheduled = scheduledModelMapper.selectById(scheduledModel.getId());
            // nextExecutionTime指向的是下一次执行的时间
            //这里每次配置就会查询数据库中的cron表达式 我们只需要更新数据库就可以了 ,如果觉得Mysql慢 也可以考虑使用Redis
            return new CronTrigger(scheduled.getCronExpression()).nextExecutionTime(triggerContext);
        });
        scheduledFutureMap.put(scheduledModel.getId(),schedule);
        scheduledModelMap.put(scheduledModel.getId(),scheduledModel);
    }
    
    

    /**
     * 取消定时任务
     */
    public void cancelTask(Integer id) {
        if (scheduledFutureMap.containsKey(id)) {
            ScheduledFuture<?> scheduledFuture = scheduledFutureMap.get(id);
            scheduledFuture.cancel(true);
            scheduledFutureMap.remove(id);
            scheduledModelMap.remove(id);
        }
    }

    /**
     * 查询现在所有的定时任务
     */
    public String findAllTask() {
        return scheduledModelMap.toString();
    }
}

四、编写ScheduleConfig类

这里开始编写配置类,虽然配置类是第一个执行,但是需要调用前面的方法,所以就按调用顺序编写的这篇文章,更方便理解

import lombok.SneakyThrows;
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.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledThreadPoolExecutor;

@Configuration
@EnableScheduling
public class ScheduledConfig implements SchedulingConfigurer {

    @Autowired
    private SpringScheduledCronRepository repository;

    @Autowired
    private ApplicationContext context;


    @SneakyThrows
    @Override
    public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
        // 初始化定时任务
        repository.initScheduledTask();
        // 其实这里也可以使用scheduledTaskRegistrar去添加任务的
        // 但是这样动态增加的时候,还是需要使用SpringScheduledCronRepository的add方法
        // 而且取消任务不方便 就放弃了在这里配置
        // 调用scheduledTaskRegistrar的方法就可以了 也很方便 看实际需求吧
    }

    @Bean
    public Executor taskExecutor() {
        return new ScheduledThreadPoolExecutor(10);
    }
}

五、Controller层编写

@RestController
@RequestMapping
public class TaskController {
    

    @Autowired
    private SpringScheduledCronRepository cronRepository;

    /**
     * 查询所有
     */
    @RequestMapping("findAll")
    public String findAll() {
        return cronRepository.findAllTask();
    }

    /**
     * 增加
     */
    @RequestMapping("addTask")
    public void addTask(@RequestBody ScheduledModel scheduledModel) {
        cronRepository.addTask(scheduledModel);
    }

    /**
     * 取消
     */
    @RequestMapping("cancel")
    public void cancel(Integer id) {
        cronRepository.cancelTask(id);    
    }
}

六、运行结果

数据库初始化数据有两条

image.png

项目启动运行结果

image.png

依次取消这两条数据,测试取消是否成功(如下图两个都取消后并无输出了)

image.png

再增加一个定时任务(再看增加之后又有定时任务了)

image.png

总结

上面就是配置定时任务的大概Demo,代码的编写还得按照自己的业务去实现,上面代码编写不一定适合你们的业务