springboot动态配置定时任务

前提条件:建表
建表sql语句

DROP TABLE IF EXISTS `scheduled_job`;

CREATE TABLE `scheduled_job` (
  `job_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键id',
  `job_key` varchar(128) COLLATE utf8_bin NOT NULL COMMENT '定时任务完整类名',
  `cron_expression` varchar(20) COLLATE utf8_bin NOT NULL COMMENT 'cron表达式',
  `job_name` varchar(20) COLLATE utf8_bin NOT NULL COMMENT '任务名称',
  `task_explain` varchar(50) COLLATE utf8_bin NOT NULL DEFAULT '' COMMENT '执行周期',
  `job_desc` varchar(500) COLLATE utf8_bin DEFAULT NULL COMMENT '任务描述',
  `status` tinyint(4) NOT NULL DEFAULT '1' COMMENT '状态,1:正常;-1:停用',
  PRIMARY KEY (`job_id`),
  UNIQUE KEY `job_key` (`job_key`),
  UNIQUE KEY `cron_key_unique_idx` (`job_key`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='定时任务表';

创建定时任务调度线程池

/**
 * @author ljm
 */
@Configuration
@Slf4j
public class ScheduledConfig {

    @Bean
    public ThreadPoolTaskScheduler threadPoolTaskScheduler() {
        log.info("创建定时任务调度线程池 start");
        ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
        threadPoolTaskScheduler.setPoolSize(60);
        threadPoolTaskScheduler.setThreadNamePrefix("taskExecutor-");
        threadPoolTaskScheduler.setWaitForTasksToCompleteOnShutdown(true);
        threadPoolTaskScheduler.setAwaitTerminationSeconds(60);
        log.info("创建定时任务调度线程池 end");
        return threadPoolTaskScheduler;
    }

}

定时任务业务类

public interface ScheduledTaskService{

    /**
     * @Author ljm
     * @Description 启动任务
     * @Date  2023/1/9
     * @return
     */
    Boolean start(ScheduledJob scheduledJob);

    /**
     * @Author ljm
     * @Description 停止任务
     * @Date  2023/1/9
     * @return
     */
    Boolean stop(String jobKey);

    /**
     * @Author ljm
     * @Description 重新启动任务
     * @Date  2023/1/9
     * @return
     */
    Boolean restart(ScheduledJob scheduledJob);

     /**
      * @Author ljm
      * @Description 初始化任务
      * @Date  2023/1/9
      * @return
      */
    void initTask();
}

定时任务业务实现类

@Slf4j
@Service
public class ScheduledTaskServiceImpl implements ScheduledTaskService {

    /**
     * 可重入锁
     */
    private ReentrantLock lock = new ReentrantLock();

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

    /**
     * 启动状态的定时任务集合
     */
    public Map<String, ScheduledFuture> scheduledFutureMap = new ConcurrentHashMap<>();

    @Autowired
    private ScheduledJobService scheduledJobService;


    /**
     * @Author ljm
     * @Description 启动任务
     * @Date  2023/1/9
     * @return
     */
    @Override
    public Boolean start(ScheduledJob scheduledJob) {
        String jobKey = scheduledJob.getJobKey();
        log.info("启动定时任务"+jobKey);
        //添加锁放一个线程启动,防止多人启动多次

        log.info("开始加锁");
        lock.lock();
        try {
            if(this.isStart(jobKey)){
                log.info("当前任务在启动状态中");
                return false;
            }
            //任务启动
            this.doStartTask(scheduledJob);
        } finally {
            lock.unlock();
            log.info("解锁完毕");
        }

        return true;
    }

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


    /**
     * @Author ljm
     * @Description 停止任务
     * @Date  2023/1/9
     * @return
     */
    @Override
    public Boolean stop(String jobKey) {
        log.info("停止任务 "+jobKey);
        boolean flag = scheduledFutureMap.containsKey(jobKey);
        log.info("当前实例是否存在 "+flag);
        if(flag){
            ScheduledFuture scheduledFuture = scheduledFutureMap.get(jobKey);

            scheduledFuture.cancel(true);

            scheduledFutureMap.remove(jobKey);
        }
        return flag;
    }

    /**
     * @Author ljm
     * @Description 重新启动任务
     * @Date  2023/1/9
     * @return
     */
    @Override
    public Boolean restart(ScheduledJob scheduledJob) {
        log.info("重启定时任务"+scheduledJob.getJobKey());
        //停止
        this.stop(scheduledJob.getJobKey());

        return this.start(scheduledJob);
    }

    /**
     * @Author ljm
     * @Description 启动任务
     * @Date  2023/1/9
     * @return
     */
    public void doStartTask(ScheduledJob sj){
        log.info(sj.getJobKey());
        if(sj.getStatus().intValue() != 1) {
            return;
        }
        Class<?> clazz;
        ScheduledOfTask task;
        try {
            clazz = Class.forName(sj.getJobKey());
            task = (ScheduledOfTask) SpringContextUtil.getBean(clazz);
        } catch (ClassNotFoundException e) {
            throw new IllegalArgumentException("spring_scheduled_cron表数据" + sj.getJobKey() + "有误", e);
        }
        Assert.isAssignable(ScheduledOfTask.class, task.getClass(), "定时任务类必须实现ScheduledOfTask接口");
        ScheduledFuture scheduledFuture = threadPoolTaskScheduler.schedule(task,(triggerContext ->
                new CronTrigger(sj.getCronExpression()).nextExecutionTime(triggerContext)));
        scheduledFutureMap.put(sj.getJobKey(),scheduledFuture);
    }

    /**
     * @Author ljm
     * @Description 初始化任务
     * @Date  2023/1/9
     * @return
     */
    @Override
    public void initTask() {
        List<ScheduledJob> list = scheduledJobService.list();
        for (ScheduledJob scheduledJob : list) {
            //未启用
            if(scheduledJob.getStatus().intValue() == -1){
                continue;
            }
            doStartTask(scheduledJob);
        }
    }


}

初始化定时任务

@Slf4j
@Component
public class ScheduledTaskRunner implements ApplicationRunner {

    @Autowired
    private ScheduledTaskService scheduledTaskService;

     /**
      * @Author ljm
      * @Description 启动项目初始化任务
      * @Date  2023/1/9
      * @return
      */
    @Override
    public void run(ApplicationArguments args) throws Exception {
        log.info("----初始化定时任务开始----");
        scheduledTaskService.initTask();
        log.info("----初始化定时任务完成----");
    }
}

执行调度任务接口

public interface ScheduledOfTask extends Runnable{

    <T> void execute() throws Exception;

    @SneakyThrows
    @Override
    default void run() {
        execute();
    }
}
@Component
 /**
  * @Author ljm
  * @Description 当一个类实现了这个接口之后,这个类就可以方便的获得ApplicationContext对象(spring上下文),
  * Spring发现某个Bean实现了ApplicationContextAware接口,Spring容器会在创建该Bean之后,自动调用该Bean的
  * setApplicationContext(参数)方法,调用该方法时,会将容器本身ApplicationContext对象作为参数传递给该方法。
  * @Date  2023/1/11 9:43
  * @return
  */
public class SpringContextUtil implements ApplicationContextAware {


    private static ApplicationContext applicationContext = null;
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        if(SpringContextUtil.applicationContext == null){
            SpringContextUtil.applicationContext  = applicationContext;
        }
    }

    public static ApplicationContext getApplicationContext() {
        return applicationContext;
    }

    public static Object getBean(String name){
        return getApplicationContext().getBean(name);
    }

    public static <T> T getBean(Class<T> clazz){
        return getApplicationContext().getBean(clazz);
    }

    public static <T> T getBean(String name,Class<T> clazz){
        return getApplicationContext().getBean(name, clazz);
    }
}

定义类实现任务接口创建调度任务

@Component
@Slf4j
public class TaskJob1 implements ScheduledOfTask {

    @Autowired
    private RedisUtil redisUtil;

    @Autowired
    private MockRules mockRules;

    @Autowired
    private TableNameService tableNameService;


    /**
     * @Author ljm
     * @Description 启动任务
     * @Date  2023/2/13 15:20
     * @return
     */
    @Override
    public <T> void execute() throws Exception {

        log.info("执行任务 1 "+ LocalDateTime.now());

        log.info("执行任务 1 完成 "+ LocalDateTime.now());

    }
}

动态修改任务

@Data
@TableName("scheduled_job")
public class ScheduledJob {

    @TableId(value = "job_id",type = IdType.AUTO)
    private Integer jobId;

    @NotBlank(message = "类名不能为空")
    private String jobKey;

    private String cronExpression;

    private String jobName;

    private String taskExplain;

    private String jobDesc;

    private Integer status;

}
@RestController
@CrossOrigin
@RequestMapping("/job")
public class ScheduledJobController {

    @Autowired
    private ScheduledJobService scheduledJobService;

    @ApiOperation("修改并重启定时任务")
    @PostMapping(value = "/update")
    public Result update(@RequestBody ScheduledJob scheduledJob){
        if(scheduledJobService.updateOne(scheduledJob)){
            return Result.ok();
        }
        return Result.fail();
    }


    @ApiOperation("停止定时任务")
    @PostMapping(value = "/stop")
    public Result stop(@RequestParam Integer jobId){
        boolean stop = scheduledJobService.stop(jobId);
        if(stop){
            return Result.ok();
        }
        return Result.fail();
    }


    public static void main(String[] args) {
        System.out.println("=======================================");
    }

    @ApiOperation("定时任务启用或停用")
    @PostMapping(value = "/jobEnable/{jobId}/{status}")
    public Result stop(@PathVariable Integer jobId,@PathVariable Integer status) throws Exception {
        boolean stop = scheduledJobService.jobEnable(jobId,status);
        if(stop){
            return Result.ok();
        }
        return Result.fail();
    }

    @ApiOperation("修改定时任务")
    @PostMapping(value = "/updateJob")
    public Result updateJob(@RequestBody ScheduledJob scheduledJob) throws Exception {
        Result result = scheduledJobService.updateJob(scheduledJob);
        return result;
    }

    @ApiOperation("定时任务查询")
    @PostMapping("/list/{pageNo}/{pageSize}")
    public Result JobList(@PathVariable("pageNo") Integer pageNo,
                          @PathVariable("pageSize") Integer pageSize,
                          @RequestBody SearchObj searchObj){
        Page<ScheduledJob> page = new Page<>(pageNo,pageSize);
        Page<ScheduledJob> list = null;
        try {
            QueryWrapper<ScheduledJob> wrapper = new QueryWrapper<>();
            wrapper.like(StringUtils.isNotBlank(searchObj.getJobKey()),"job_key",searchObj.getJobKey());
            wrapper.like(StringUtils.isNotBlank(searchObj.getJobName()),"job_name",searchObj.getJobName());
            list = scheduledJobService.page(page,wrapper);
            return Result.ok(list);
        } catch (Exception e) {
            return Result.fail();
        }
    }

    @ApiOperation("创建定时任务")
    @PostMapping("createJob")
    public Result createJob(@Valid @RequestBody ScheduledJob scheduledJob){
        Boolean scheduledJobs = scheduledJobService.createJob(scheduledJob);
        if(scheduledJobs){
            return Result.ok();
        }
        return Result.fail();
    }

}


public interface ScheduledJobService extends IService<ScheduledJob> {

    /**
     * 修改定时任务,并重新启动
     * @param scheduledJob
     * @return
     */
    boolean updateOne(ScheduledJob scheduledJob);

     /**
      * @Author ljm
      * @Description 停止定时任务
      */
    boolean stop(Integer jobId);

     /**
      * @Author ljm
      * @Description 定时任务启用或停用
      * @Date  2023/1/9
      * @return
      */
    boolean jobEnable(Integer jobId, Integer status) throws Exception;

     /**
      * @Author ljm
      * @Description 修改定时任务
      * @Date  2023/1/9
      * @return
      */
     Result updateJob(ScheduledJob scheduledJob) throws Exception;

      /**
       * @Author ljm
       * @Description 创建定时任务
       * @Date  2023/1/11
       * @return
       */
    Boolean createJob(ScheduledJob scheduledJob);

}

@Service
@Slf4j
public class ScheduledJobServiceImpl extends ServiceImpl<ScheduledJobMapper, ScheduledJob> implements ScheduledJobService {

    @Autowired
    private ScheduledTaskService scheduledTaskService;


    /**
     * 修改定时任务,并重新启动
     * @param scheduledJob
     * @return
     */
    @Override
    public boolean updateOne(ScheduledJob scheduledJob) {
        boolean update = updateById(scheduledJob);
        if(update){
            scheduledTaskService.restart(getById(scheduledJob.getJobId()));
        }
        return true;
    }


    /**
     * @Author ljm
     * @Description 停止定时任务
     */
    @Override
    public boolean stop(Integer jobId) {
        ScheduledJob scheduledJob = baseMapper.selectById(jobId);
        Boolean stop = scheduledTaskService.stop(scheduledJob.getJobKey());
        return stop;
    }


    /**
     * @Author ljm
     * @Description 定时任务启用或停用
     * @Date  2023/1/9
     * @return
     */
    @Override
    public boolean jobEnable(Integer jobId, Integer status) throws Exception {
        Boolean start = true;
        Boolean stop = true;
        ScheduledJob scheduledJob = baseMapper.selectById(jobId);
        scheduledJob.setStatus(status);
        baseMapper.updateById(scheduledJob);
        if (status == 1){
            start = scheduledTaskService.start(scheduledJob);
        }
        if (status == 0){
            stop = scheduledTaskService.stop(scheduledJob.getJobKey());
        }
        return start && stop;
    }


    /**
     * @Author ljm
     * @Description 修改定时任务
     * @Date  2023/1/9
     * @return
     */
    @Override
    public Result updateJob(ScheduledJob scheduledJob) throws Exception {
        Integer jobId = scheduledJob.getJobId();
        ScheduledJob selectById = baseMapper.selectById(jobId);
        Integer status = selectById.getStatus();
        if (status == 1){
            return Result.ok("任务启动中,不能修改");
        }
        int update = baseMapper.updateById(scheduledJob);
        return Result.ok(update>0);
    }


    /**
     * @Author ljm
     * @Description 创建定时任务
     * @Date  2023/1/11
     * @return
     */
    @Override
    public Boolean createJob(ScheduledJob scheduledJob) {
        scheduledJob.setStatus(0);
        boolean save = save(scheduledJob);
        return save;
    }
}

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值