Spring Boot 动态 修改定时任务task

  |   0 评论   |   0 浏览

表结构

CREATE TABLE `t_task`  (
  `id` int(30) NOT NULL AUTO_INCREMENT,
  `table_name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  `job_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `class_name` varchar(60) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `method` varchar(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `cron` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '-' COMMENT '设置为  -  , 则表示关闭自动执行。',
  `status` int(2) NULL DEFAULT 0,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;

对应实体类

package cn.lacknb.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;

/**
 * @author gitsilence
 * @version 1.0
 * @date 2020/10/4 0004 下午 17:03
 * @mail gitsilence@lacknb.cn
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Task implements Serializable {

    private Integer id;
    private String tableName;
    private String cron;
    private String jobName;
    private String className;
    private String method;
    private Integer status;

}

1. 第一种 不支持 实时更改。

这种,我们每次更改cron表达式之后,需要重启服务器 才能生效。

创建ApplicationContextHelper, 用来通过类名获取对应bean

package cn.lacknb.schedule.config;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

/**
 * @author gitsilence
 * @version 1.0
 * @date 2020/10/4 0004 下午 18:04
 * @mail gitsilence@lacknb.cn
 */
@Component
public class ApplicationContextHelper implements ApplicationContextAware {
    private static ApplicationContext applicationContext;

    public ApplicationContextHelper() {
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        ApplicationContextHelper.applicationContext = applicationContext;
    }

    public static Object getBean(String beanName) {
        return applicationContext != null?applicationContext.getBean(beanName):null;
    }

}

创建ScheduleSetting

package cn.lacknb.schedule.config;

import cn.lacknb.pojo.Task;
import cn.lacknb.service.TaskService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.Trigger;
import org.springframework.scheduling.TriggerContext;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Component;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Method;
import java.util.Date;
import java.util.List;

/**
 * @author gitsilence
 * @version 1.0  这种方式不支持 实时更改 cron 表达式。
 * @date 2020/10/4 0004 下午 17:55
 * @mail gitsilence@lacknb.cn
 */
@Component
public class ScheduleSetting implements SchedulingConfigurer {

    @Autowired
    private TaskService taskService;

    @Override
    public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
        // 获取 所有的任务
        List<Task> tasks = taskService.selectTask();
        System.out.println("total tasks count: " + tasks.size());
        for (Task task : tasks) {
            scheduledTaskRegistrar.addTriggerTask(getRunnable(task), getTrigger(task));
        }

    }

    /**
     * 转换首字母小写
     *
     * @param str
     * @return
     */
    public static String lowerFirstCapse(String str) {
        char[] chars = str.toCharArray();
        chars[0] += 32;
        return String.valueOf(chars);
    }


    private Runnable getRunnable (Task task) {
        System.out.println("runnable setting success");
        return new Runnable() {
            @Override
            public void run() {
                Class<?> clazz;
                try {
                    clazz = Class.forName(task.getClassName());
                    String className = lowerFirstCapse(clazz.getSimpleName());
                    System.out.println("simpleName: " + clazz.getSimpleName());
                    Object bean = ApplicationContextHelper.getBean(className);
                    Method method = ReflectionUtils.findMethod(bean.getClass(), task.getMethod());
                    ReflectionUtils.invokeMethod(method, bean);
                } catch (ClassNotFoundException e){
                    e.printStackTrace();
                }
            }
        };
    }

    private Trigger getTrigger (Task task) {
        System.out.println("getTrigger setting success");
        return new Trigger() {
            @Override
            public Date nextExecutionTime(TriggerContext triggerContext) {
                CronTrigger trigger = new CronTrigger(task.getCron());
                Date nextExec = trigger.nextExecutionTime(triggerContext);
                return nextExec;
            }
        };
    }

}

第二种 支持 实时更改。

这种方式,支持 实时更改。 当我们进行增加、修改任务的时候,需要 把之前的调度任务取消掉,然后再重新开始。

创建接口 ScheduleInterface

package cn.lacknb.schedule.Itf;

/**
 * @author gitsilence
 * @version 1.0
 * @date 2020/10/4 0004 下午 19:20
 * @mail gitsilence@lacknb.cn
 */
public interface ScheduleInterface {

    void start ();

    void stop ();

}

创建ScheduleItfImpl 实现 上面 接口

这里需要 第一种的 ApplicationContextHelper

package cn.lacknb.schedule.Itf;

import cn.lacknb.pojo.Task;
import cn.lacknb.schedule.config.ApplicationContextHelper;
import cn.lacknb.service.TaskService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.Trigger;
import org.springframework.scheduling.TriggerContext;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Service;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.ScheduledFuture;

/**
 * @author gitsilence
 * @version 1.0
 * @date 2020/10/4 0004 下午 19:21
 * @mail gitsilence@lacknb.cn
 */
@Service
public class ScheduleItfImpl implements ScheduleInterface {

    @Autowired
    private TaskScheduler taskScheduler;

    private static Set<ScheduledFuture> set;

    static {
        System.out.println("static code is running ~ ~");
        // 线程安全的 列表
        set = Collections.synchronizedSet(new HashSet<>());
    }

//    private ScheduledFuture scheduledFuture;

    @Autowired
    private TaskService taskService;

    /**
     * 转换首字母小写
     *
     * @param str
     * @return
     */
    public static String lowerFirstCapse(String str) {
        char[] chars = str.toCharArray();
        chars[0] += 32;
        return String.valueOf(chars);
    }

    @Override
    public void start() {
        System.out.println("start method ~ ~ ~");
        List<Task> tasks = taskService.selectTask();
        System.out.println("total tasks: " + tasks.size());
        for (Task task : tasks) {
            ScheduledFuture<?> schedule = taskScheduler.schedule(new Runnable() {
                @Override
                public void run() {
                    Class<?> clazz;
                    try {
                        clazz = Class.forName(task.getClassName());
                        String className = lowerFirstCapse(clazz.getSimpleName());
                        System.out.println("simpleName: " + clazz.getSimpleName());
                        Object bean = ApplicationContextHelper.getBean(className);
                        Method method = ReflectionUtils.findMethod(bean.getClass(), task.getMethod());
                        ReflectionUtils.invokeMethod(method, bean);
                    } catch (ClassNotFoundException e) {
                        e.printStackTrace();
                    }
                }
            }, new Trigger() {
                @Override
                public Date nextExecutionTime(TriggerContext triggerContext) {
                    CronTrigger trigger = new CronTrigger(task.getCron());
                    Date nextExec = trigger.nextExecutionTime(triggerContext);
                    return nextExec;
                }
            });
            set.add(schedule);
        }
    }

    @Override
    public void stop() {
        if (set.size() != 0) {
            for (ScheduledFuture scheduledFuture : set) {
                scheduledFuture.cancel(false);
            }
        }
    }
}

将该类 设置 为 随着服务器启动 而启动。

package cn.lacknb.schedule.config;

import cn.lacknb.schedule.Itf.ScheduleInterface;
import cn.lacknb.schedule.Itf.ScheduleItfImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.util.Date;

/**
 * @author gitsilence
 * @version 1.0  继承Application接口 项目 启动时 会按照执行顺序执行run方法
 *
 *  通过设置order的value来指定执行的顺序
 * @date 2020/10/4 0004 下午 19:33
 * @mail gitsilence@lacknb.cn
 */
@Component
@Order(value = 1)
public class InitialSchedulerService implements ApplicationRunner {

    @Autowired
    private ScheduleInterface scheduleInterface;

    /**
     * 项目启动的时候,启动该方法。
     * @param args
     * @throws Exception
     */
    @Override
    public void run(ApplicationArguments args) throws Exception {
        System.out.println(new Date());
        scheduleInterface.start();
    }
}


标题:Spring Boot 动态 修改定时任务task
作者:MrNiebit
地址:https://blog.lacknb.cn/articles/2020/10/04/1601813465391.html