蟑螂恶霸的博客 蟑螂恶霸的博客
首页
  • Web自动化
  • 自动化测试框架
  • 接口自动化
  • 测试面试题
  • 技术文档
  • GitHub技巧
  • 博客搭建
  • Vue
  • JavaScript
  • Nginx
  • 自动化测试
  • 学习
  • 面试
  • 心情杂货
  • 实用技巧
  • 友情链接
关于
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

蟑螂恶霸

一个爱折腾的程序员,什么都想试试!
首页
  • Web自动化
  • 自动化测试框架
  • 接口自动化
  • 测试面试题
  • 技术文档
  • GitHub技巧
  • 博客搭建
  • Vue
  • JavaScript
  • Nginx
  • 自动化测试
  • 学习
  • 面试
  • 心情杂货
  • 实用技巧
  • 友情链接
关于
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 常用工具类

    • Session登录失败限制 & RSA密码加密
    • java配置双数据库
    • 原生jdbc工具类
    • orcal下划线转驼峰
    • RSA加密工具类
    • 实现定时任务数据库配置
      • 使用数据库配置定时任务做任务调度
        • 定时任务调度表
        • 定时任务实体类
        • 操作定时任务的mapper
        • 定时任务配置在spring
        • 定时任务实现接口
        • 未引入hutool时 使用的SpringUtil
        • 定时任务类
        • cron转换成时间
        • 时间转换成cron
        • 方案二 修改就去更新线程池 做到同步定时任务
        • ScheduledFutureHolder
  • 框架知识

  • 基础技巧

  • java知识集锦
  • 常用工具类
蟑螂恶霸
2023-06-09
目录

实现定时任务数据库配置

# 使用数据库配置定时任务做任务调度

一般来说任务调度是开发写在代码里,或者用任务调度的框架。但是有的项目肯定是已经是开发中的,不能支持重新构造,那么就需要这种配置。在数据库编辑cron实现不终止项目的情况来更改任务调度的时间。

# 定时任务调度表

首先肯定需要一个定时任务表来记录并控制你的定时任务。

CREATE TABLE `spring_scheduled_cron` (
  `cron_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键id',
  `cron_key` varchar(128) NOT NULL COMMENT '定时任务完整类名',
  `cron_expression` varchar(20) NOT NULL COMMENT 'cron表达式',
  `task_explain` varchar(50) NOT NULL DEFAULT '' COMMENT '任务描述',
  `status` tinyint(4) NOT NULL DEFAULT '1' COMMENT '状态,1:正常;0:停用',
  PRIMARY KEY (`cron_id`),
  UNIQUE KEY `cron_key` (`cron_key`),
  UNIQUE KEY `cron_key_unique_idx` (`cron_key`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COMMENT='定时任务表';
1
2
3
4
5
6
7
8
9
10

对应的表数据要记录在此,但是加了数据之后代码里面必须要有相应的定时任务类,不然项目无法启动。

INSERT INTO `spring_scheduled_cron` (`cron_id`, `cron_key`, `cron_expression`, `task_explain`, `status`)
VALUES (1, 'com.example.test.task.testTask', '0/5 * * * * ? ', '测试定时任务', 0);
1
2

# 定时任务实体类


/**
 * @Description  
 * @Author  wby
 * @Date 2022-04-21 
 */

@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName ( "spring_scheduled_cron" )
public class SpringScheduledCron  implements Serializable {

	private static final long serialVersionUID =  6867905359879360779L;

	/**
	 * 主键id
	 */
   	@TableId( "cron_id" )
	private Long cronId;

	/**
	 * 定时任务完整类名
	 */
   	@TableField( "cron_key" )
	private String cronKey;

	/**
	 * cron表达式
	 */
   	@TableField( "cron_expression" )
	private String cronExpression;

	/**
	 * 任务描述
	 */
   	@TableField( "task_explain" )
	private String taskExplain;

	/**
	 * 状态,1:正常;0:停用
	 */
   	@TableField( "status" )
	private Integer status;

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46

# 操作定时任务的mapper

@Component
@Mapper
public interface SpringScheduledCronMapper extends BaseMapper<SpringScheduledCron> {

    @Select("select * from spring_scheduled_cron where cron_key = #{cronKey}")
    SpringScheduledCron findByCronKey(String cronKey);
}
1
2
3
4
5
6
7

# 定时任务配置在spring

import com.example.yiwu.mysqlMapper.SpringScheduledCronMapper;
import com.example.yiwu.pojo.SpringScheduledCron;
import com.example.yiwu.task.ScheduledOfTask;
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.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.util.Assert;

import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

/**
 * @author wby
 * @date 2022/4/22
 */
@Configuration
public class ScheduledConfig implements SchedulingConfigurer {
    @Autowired
    private ApplicationContext context;

    @Autowired
    private SpringScheduledCronMapper springScheduledCronMapper;

    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        for (SpringScheduledCron springScheduledCron : springScheduledCronMapper.selectList(null)) {
            Class<?> clazz;
            Object task;
            try {
                clazz = Class.forName(springScheduledCron.getCronKey());
                task = context.getBean(clazz);
            } catch (ClassNotFoundException e) {
                throw new IllegalArgumentException("spring_scheduled_cron表数据" + springScheduledCron.getCronKey() + "有误", e);
            } catch (BeansException e) {
                throw new IllegalArgumentException(springScheduledCron.getCronKey() + "未纳入到spring管理", e);
            }
            Assert.isAssignable(ScheduledOfTask.class, task.getClass(), "定时任务类必须实现ScheduledOfTask接口");
            // 可以通过改变数据库数据进而实现动态改变执行周期
            taskRegistrar.addTriggerTask(((Runnable) task),
                    triggerContext -> {
                        String cronExpression = springScheduledCronMapper.selectById(springScheduledCron.getCronId()).getCronExpression();
                        return new CronTrigger(cronExpression).nextExecutionTime(triggerContext);
                    }
            );
        }
    }

    @Bean
    public Executor taskExecutor() {
        return Executors.newScheduledThreadPool(10);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57

# 定时任务实现接口

import cn.hutool.extra.spring.SpringUtil;
import com.datanew.mapper.dfxx.SpringScheduledCronMapper;
import com.datanew.model.zcxm.SpringScheduledCron;

import java.util.Objects;

/**
 * @Author 929
 * @Date 2023/6/7 0007
 */
public interface ScheduledOfTask extends Runnable {

    /**
     * 定时任务方法
     */
    void execute();

    /**
     * 实现控制定时任务启用或禁用的功能
     */
    @Override
    default void run() {
        SpringScheduledCronMapper repository = SpringUtil.getBean(SpringScheduledCronMapper.class);
        SpringScheduledCron scheduledCron = repository.findByCronKey(this.getClass().getName());
        if (Objects.equals(scheduledCron.getStatus(),0)) {
            // 任务是禁用状态
            return;
        }
        execute();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

# 未引入hutool时 使用的SpringUtil

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

@Component
public class SpringUtils implements ApplicationContextAware {

    private static ApplicationContext context;

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

    public static <T> T getBean(Class<T> clz) {
        return context.getBean(clz);
    }

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

    public ApplicationContext getApplicationContext() {
        return context;
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

# 定时任务类

定时任务类必须实现ScheduledOfTask。不然会报错!!!!

@Component
@Slf4j
public class testTask implements ScheduledOfTask {
    
    @Override
    public void execute() {
        log.info("=========================定时任务内容==============================");
    }
}
1
2
3
4
5
6
7
8
9

这样你的定时任务就可以在数据库进行控制了!

# cron转换成时间

public class DateUtils {

    /**
     * 验证cron表达式是否正确
     * @param cronExpression
     * @return
     */
    public static boolean isValid(String cronExpression){
        return CronExpression.isValidExpression(cronExpression);
    }

    /**
     * 将日期转换为cron时间
     *
     * @param date
     * @return
     */
    public static String parseCron(Date date) {
        String format="ss mm HH dd MM ? yyyy";
        SimpleDateFormat dateFormat = new SimpleDateFormat(format);
        return dateFormat.format(date);
    }

    /**
     * 获取下一次的执行时间
     *
     * @param cron
     * @return
     * @throws ParseException
     */
    public static Date nextTime(String cron, Date date) throws ParseException {
        // 加载包之后直接引用这个方法
        CronExpression cronExpression = new CronExpression(cron);
        // 转换 new Date 是为了给最近一次执行时间一个初始时间,这里给当前时间
        Date nextTime = cronExpression.getNextValidTimeAfter(date);
        return nextTime;
    }

    /**
     * 获取10个下次执行时间,如果不足10次,则返回实际次数
     * @param cron
     * @param date
     * @return
     * @throws ParseException
     */
    public static List<Date> next10Times(String cron, Date date) throws ParseException {
        return nextTimes(cron,date,10);
    }

    /**
     * 获取n个下次执行时间,如果不足n次,则返回实际次数
     * @param cron
     * @param date
     * @param n
     * @return
     * @throws ParseException
     */
    public static List<Date> nextTimes(String cron, Date date, int n) throws ParseException {
        List<Date> nextTimes = new ArrayList<>();
        Date nextTime = date;
        for (int i = 0; i < n; i++) {
            Date date1 = nextTime(cron, nextTime);
            if (null == date1) {
                break;
            }
            nextTimes.add(date1);
            nextTime = date1;
        }
        return nextTimes;
    }

    /**
     * 时间转字符串
     * @param date
     * @return
     */
    public static String parseDate(Date date){
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        String dateFormat = sdf.format(date);
        return dateFormat;
    }

    /**
     * 字符串转时间
     * @param dateString
     * @return
     * @throws ParseException
     */
    public static Date toDate(String dateString) throws ParseException {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        Date dateFormatParse = sdf.parse(dateString);
        return dateFormatParse;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94

# 时间转换成cron

public class CronUtils {

    /**
     * 每年时间format格式
     */
    public static final String DATEFORMAT_YEAR = "ss mm HH dd MM ? yyyy";

    /**
     * 每天时间format格式
     */
    public static final String DATEFORMAT_EVERYDAY = "ss mm HH * * ?";

    /**
     * 每周时间format格式
     */
    public static final String SUNDAY = "ss mm HH ? * 1";
    public static final String MONDAY = "ss mm HH ? * 2";
    public static final String TUESDAY = "ss mm HH ? * 3";
    public static final String WEDNESDAY = "ss mm HH ? * 4";
    public static final String THURSDAY = "ss mm HH ? * 5";
    public static final String FRIDAY = "ss mm HH ? * 6";
    public static final String SATURADY = "ss mm HH ? * 7";

    public static String formatDateByPattern(Date date, String dateFormat) {
        SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);
        String formatTimeStr = null;
        if (date != null) {
            formatTimeStr = sdf.format(date);
        }
        return formatTimeStr;
    }

    /**
     * 时间转换时间表达式
     */
    public static String getCron(Date date, String dateFormat) {
        return formatDateByPattern(date, dateFormat);
    }


    public static void main(String[] args) throws Exception {
        String date = "14:30:00";
        Date parse = DateUtil.parse(date);
        String cron = getCron(parse, SUNDAY);
        System.out.println("每周六执行: " + cron);

        String cron1 = getCron(parse, DATEFORMAT_EVERYDAY);
        System.out.println("每天执行: " + cron1);

        String cron2 = getCron(new Date(), DATEFORMAT_YEAR);
        System.out.println("执行一次: " + cron2);

        Date date1 = DateUtils.nextTime(cron1, new Date());
        System.out.println(DateUtils.parseDate(date1));

    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57

后两个是工具类可以互相转换,可以合并成一个,只是我太懒了,懒得搞。哈哈哈哈哈哈哈哈哈哈哈哈哈!

# 方案二 修改就去更新线程池 做到同步定时任务

@Profile("devt")
@Component
@Slf4j
public class ScheduleUtil implements InitializingBean, DisposableBean, ApplicationContextAware {

    private final ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();

    //存储任务执行的包装类
    private final ConcurrentHashMap<String, ScheduledFutureHolder> scheduleMap = new ConcurrentHashMap<>();

    private ApplicationContext applicationContext;

    @Autowired
    private SpringScheduledCronMapper springScheduledCronMapper;

    /**
     * 启动任务
     * 如果不想手动触发任务可以使用 @PostConstruct注解来启动
     */
    public void startTask(String taskName, String cron) {
        try {
            // 判断当前任务是否已经在计划列表,在的话不再重复加载
            if (scheduleMap != null && scheduleMap.containsKey(taskName)) {
                this.restartTask(taskName, cron);
            } else {
                // 将任务交给任务调度器执行
                Runnable task = (Runnable) applicationContext.getBean(Class.forName(taskName));
                ScheduledFuture<?> schedule = threadPoolTaskScheduler.schedule(task, new CronTrigger(cron));
                // 将任务包装成ScheduledFutureHolder
                ScheduledFutureHolder scheduledFutureHolder = new ScheduledFutureHolder();
                scheduledFutureHolder.setScheduledFuture(schedule);
                scheduledFutureHolder.setTask(task);
                scheduledFutureHolder.setCorn(cron);
                scheduleMap.put(scheduledFutureHolder.getTask().getClass().getName(), scheduledFutureHolder);
                log.info("定时任务启动成功,任务名:[{}],cron:[{}]", taskName, cron);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 查询所有的任务
     */
    @RequestMapping("/queryTask")
    public void queryTask() {
        scheduleMap.forEach((k, v) -> {
            System.out.println(k + "  " + v);
        });
    }

    /**
     * 停止任务
     *
     * @param className
     */
    @RequestMapping("/stop/{className}")
    public void stopTask(@PathVariable String className) {
        if (scheduleMap.containsKey(className)) {//如果包含这个任务
            ScheduledFuture<?> scheduledFuture = scheduleMap.get(className).getScheduledFuture();
            if (scheduledFuture != null) {
                scheduledFuture.cancel(true);
            }
        }
    }


    /**
     * 重启任务,修改任务的触发时间
     *
     * @param taskName,cron
     * @throws InstantiationException
     * @throws IllegalAccessException
     */
    public void restartTask(String taskName, String cron) {
        ScheduledFutureHolder scheduledFutureHolder = scheduleMap.get(taskName);
        ScheduledFuture<?> scheduledFuture = scheduledFutureHolder.getScheduledFuture();
        if (scheduledFuture != null) {
            // 先停掉任务
            scheduledFuture.cancel(true);
            // 修改触发时间重新启动任务
            Runnable runnable = scheduledFutureHolder.getTask();
            ScheduledFuture<?> schedule = threadPoolTaskScheduler.schedule(runnable, new CronTrigger(cron));
            scheduledFutureHolder.setScheduledFuture(schedule);
            scheduledFutureHolder.setCorn(cron);
            scheduleMap.put(scheduledFutureHolder.getTask().getClass().getName(), scheduledFutureHolder);
            log.info("定时任务重新配置成功成功,任务名:[{}],cron:[{}]", taskName, cron);
        }
    }

    @Override
    public void destroy() {
        threadPoolTaskScheduler.shutdown();
    }

    @Override
    public void afterPropertiesSet() {
        threadPoolTaskScheduler.setPoolSize(10);
        threadPoolTaskScheduler.setThreadNamePrefix("schedule-util-");
        threadPoolTaskScheduler.afterPropertiesSet();

        try {
            for (SpringScheduledCron springScheduledCron : springScheduledCronMapper.selectList(null)) {
                if (springScheduledCron.getStatus() != 0) {
                    this.startTask(springScheduledCron.getCronKey(), springScheduledCron.getCronExpression());
                }
            }
        } catch (Exception exception) {
            throw new BizException(ResultCode.INTERNAL_ERROR, exception.getMessage());
        }
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117

# ScheduledFutureHolder

public class ScheduledFutureHolder {
    private ScheduledFuture<?> scheduledFuture;

    private Runnable task;

    private String corn;

    public ScheduledFuture<?> getScheduledFuture() {
        return scheduledFuture;
    }

    public void setScheduledFuture(ScheduledFuture<?> scheduledFuture) {
        this.scheduledFuture = scheduledFuture;
    }

    public String getCorn() {
        return corn;
    }

    public void setCorn(String corn) {
        this.corn = corn;
    }

    public Runnable getTask() {
        return task;
    }

    public void setTask(Runnable task) {
        this.task = task;
    }

    @Override
    public String toString() {
        return "ScheduledFutureHolder{" +
                "scheduledFuture=" + scheduledFuture +
                ", task=" + task +
                ", corn='" + corn + '\'' +
                '}';
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
上次更新: 2023/07/03, 11:02:01
RSA加密工具类
SQL Server连接

← RSA加密工具类 SQL Server连接→

最近更新
01
SQL Server连接
02-22
02
RSA加密工具类
02-22
03
java常用技巧
12-12
更多文章>
Theme by Vdoing | Copyright © 2022-2023 蟑螂恶霸 | Blog
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式