1、设计定时任务数据库(mysql)
CREATE TABLE `sys_scheduled` (
`id` varchar(32) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL COMMENT '主键',
`job_name` varchar(200) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL COMMENT '定时任务名称',
`class_name` varchar(200) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL COMMENT '类名',
`method` varchar(250) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL COMMENT '方法名',
`cron` varchar(200) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL COMMENT '定时任务表达式',
`start_flag` tinyint(1) NULL DEFAULT NULL COMMENT '启用标记 1启用 0停用',
`create_date` varchar(32) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL COMMENT '创建日期',
`create_by` varchar(32) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL COMMENT '创建人',
`update_by` varchar(32) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL COMMENT '更新人',
`update_date` varchar(32) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL COMMENT '更新日期',
`del_flag` varchar(32) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL COMMENT '删除标记',
`temp` varchar(2500) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL COMMENT '备注',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = MyISAM AUTO_INCREMENT = 1 CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci COMMENT = '定时任务配置表' ROW_FORMAT = Dynamic;
2、编写批量获取定时任务配置查询服务SysScheduledService
package com.jdsoft.springbootmybatisplusgenrator.service.crm.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jdsoft.springbootmybatisplusgenrator.bean.scheduled.SysScheduled;
import com.jdsoft.springbootmybatisplusgenrator.dao.crm.ScheduledMapper;
import com.jdsoft.springbootmybatisplusgenrator.service.crm.SysScheduledService;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* @author: wxf
* Date: 2023/8/2
* Time: 17:16
* Description:
*/
@Service
public class SysScheduledServiceImpl extends ServiceImpl<ScheduledMapper,SysScheduled> implements SysScheduledService {
@Override
public List<SysScheduled> getAllConfig() {
return this.getBaseMapper().getAllConfig();
}
@Override
public SysScheduled getById(String id) {
LambdaQueryWrapper<SysScheduled> lw = new LambdaQueryWrapper<>();
lw.eq(SysScheduled::getId,id);
return this.getBaseMapper().selectOne(lw);
}
}
2.1 po SysScheduled
package com.jdsoft.springbootmybatisplusgenrator.bean.scheduled;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
import java.io.Serializable;
@Data
public class SysScheduled implements Serializable {
@TableId(value = "id",type = IdType.ID_WORKER_STR)
private String id; // 主键
@TableId(value = "job_name") //表里的字段名称映射到实体类
private String jobName; // 定时任务名称
@TableId(value = "class_name")
private String className; // 类名
@TableId(value = "method")
private String method; // 方法名
@TableId(value = "cron")
private String cron; // 定时任务表达式
@TableId(value = "start_flag")
private Boolean startFlag; // 启用标记
@TableId(value = "temp")
private String temp;//备注
}
2.2 mybatisPlus业务mapper ScheduledMapper
package com.jdsoft.springbootmybatisplusgenrator.dao.crm;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.jdsoft.springbootmybatisplusgenrator.bean.scheduled.SysScheduled;
import org.apache.ibatis.annotations.Select;
import java.util.List;
/**
* @author: wxf
* Date: 2023/8/2
* Time: 17:14
* Description: 定时任务配置 mapper
*/
public interface ScheduledMapper extends BaseMapper<SysScheduled> {
@Select("SELECT* FROM sys_scheduled u ")
List<SysScheduled> getAllConfig();
}
3、在启动容器中添加@EnableScheduling注解
4、增加调度任务配置类实现 SchedulingConfigurer, AsyncConfigurer接口
说明:springboot项目启动会执行configureTasks实现方法代码
package com.scheduled;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ScheduledFuture;
import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.druid.util.StringUtils;
import com.jdsoft.springbootmybatisplusgenrator.bean.scheduled.SysScheduled;
import com.jdsoft.springbootmybatisplusgenrator.service.crm.SysScheduledService;
import com.wxf.springdemo.redis.RedisUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.config.CronTask;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.stereotype.Component;
import org.springframework.util.ReflectionUtils;
/**
* @Package: com.scheduled
* @ClassName: ScheduleSetting
* @Author: wxf
* @Description: 任务调度
* @Date: 2023/8/1 20:11
* @Version: 1.0
*/
@Component
public class ScheduleSetting implements SchedulingConfigurer, AsyncConfigurer {
@Autowired
private SysScheduledService sysScheduledService;
private volatile ScheduledTaskRegistrar scheduledTaskRegistrar;
//调度器map
private Map<String, ScheduledFuture<?>> scheduledFutures = new HashMap<String, ScheduledFuture<?>>();
// 执行任务map
private Map<String, CronTask> cronTasks = new HashMap<String, CronTask>();
@Autowired
private RedisUtil redisUtil;
private static List<SysScheduled> scheduleList;
@Override
public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
//异步执行定时任务
TaskScheduler taskScheduler = taskScheduler();
scheduledTaskRegistrar.setTaskScheduler(taskScheduler);
this.scheduledTaskRegistrar = scheduledTaskRegistrar;
scheduleList = sysScheduledService.getAllConfig();
redisUtil.setSysScheduledList("scheduleList", scheduleList);
refresh(scheduleList);
}
/**
* 刷新任务列表
*
* @param scheduleList 待执行的任务列表
*/
public void refresh(List<SysScheduled> scheduleList) {
//取消停用的定时策略
Set<String> keyIds = scheduledFutures.keySet();
CopyOnWriteArrayList<String> copyKeyIds = CollectionUtil.newCopyOnWriteArrayList(keyIds);
for (String scheduledFutureId : copyKeyIds) {
if (!taskExit(scheduleList, scheduledFutureId)) {
//cancel 参数 false 表示如果调度器当前执行任务时 不进行打断 true表示直接打断进程取消策略
scheduledFutures.get(scheduledFutureId).cancel(false);
scheduledFutures.remove(scheduledFutureId);
cronTasks.remove(scheduledFutureId);
}
}
if (CollectionUtil.isNotEmpty(scheduleList)) {
for (SysScheduled sysScheduled : scheduleList) {
//定时corn表达式
String cron = sysScheduled.getCron();
String keyId = sysScheduled.getId();
//任务状态 在新增任务时进行判断
Boolean startFlag = sysScheduled.getStartFlag();
// 如果启动状态是false的话 , taskExit已进行处理,不需要再进行操作,直接跳下一次循环
if (!startFlag) {
continue;
}
if (StringUtils.isEmpty(cron)) {
continue;
}
//定时任务存在的话,并且没发生改变的话不进行处理
if (scheduledFutures.containsKey(keyId) &&
cronTasks.get(keyId).getExpression().equalsIgnoreCase(cron)) {
continue;
} else if (scheduledFutures.containsKey(keyId)) {
//任务调度时间发生改变,取消当前策略的任务
scheduledFutures.get(keyId).cancel(false);
scheduledFutures.remove(keyId);
cronTasks.remove(keyId);
}
//新建定时任务
CronTask task = new CronTask(getRunnable(sysScheduled), cron);
//将定时任务分配到对应的调度器
ScheduledFuture<?> scheduledFuture = scheduledTaskRegistrar.getScheduler().
schedule(task.getRunnable(), task.getTrigger());
//保存缓存任务map
cronTasks.put(keyId, task);
//保存缓存调度器 map
scheduledFutures.put(keyId, scheduledFuture);
}
}
}
/**
* 判断调度器缓存map中是否存在查询到的配置调度任务
*
* @param scheduleList 数据库查询的调度任务
* @param taskId 主键id
* @return 判断结果
*/
private boolean taskExit(List<SysScheduled> scheduleList, String taskId) {
if (CollectionUtil.isEmpty(scheduleList) || StringUtils.isEmpty(taskId)) {
return false;
}
for (SysScheduled sysScheduled : scheduleList) {
//id存在 且任务状态是开启状态 判断任务存在
if (taskId.equalsIgnoreCase(sysScheduled.getId()) && sysScheduled.getStartFlag()) {
return true;
}
}
return false;
}
/**
* 设置异步线程池 任务调度器
*
* @return 返回任务调度器对象
*/
public ThreadPoolTaskScheduler taskScheduler() {
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.initialize();
//设置线程池数量
scheduler.setPoolSize(8);
scheduler.setThreadNamePrefix("Mq-Listeners");
//等待终止时间
scheduler.setAwaitTerminationSeconds(60);
//等待任务完成关闭
scheduler.setWaitForTasksToCompleteOnShutdown(true);
return scheduler;
}
/**
* 转换首字母小写
*
* @param str
* @return
*/
public static String lowerFirstCapse(String str) {
char[] chars = str.toCharArray();
chars[0] += 32;
return String.valueOf(chars);
}
/**
* runnable
*
* @param scheduleConfig
* @return
*/
private Runnable getRunnable(final SysScheduled scheduleConfig) {
return new Runnable() {
@Override
public void run() {
Class<?> clazz;
try {
//根据id查询对应的 启动状态
clazz = Class.forName(scheduleConfig.getClassName());
String className = lowerFirstCapse(clazz.getSimpleName());
Object bean = (Object) ApplicationContextHelper.getBean(className);
Method method = ReflectionUtils.findMethod(bean.getClass(), scheduleConfig.getMethod());
ReflectionUtils.invokeMethod(method, bean);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
};
}
}