SpringBoot动态定时任务--利用hutool工具类实现

1、创建表

CREATE TABLE `dev_job` (`ID` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,`NAME` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '名称',`CODE` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '编码',`CATEGORY` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '分类',`ACTION_CLASS` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '任务类名',`CRON_EXPRESSION` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT 'cron表达式',`JOB_STATUS` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '任务状态',`SORT_CODE` int(11) DEFAULT NULL COMMENT '排序码',`EXT_JSON` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT '扩展信息',`DELETE_FLAG` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '删除标志',`CREATE_TIME` datetime DEFAULT NULL COMMENT '创建时间',`CREATE_USER` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '创建用户',`UPDATE_TIME` datetime DEFAULT NULL COMMENT '修改时间',`UPDATE_USER` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '修改用户',PRIMARY KEY (`ID`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC COMMENT='定时任务';

2、实现代码

2.1、定时器执行者接口

定时器执行者接口,定时器都要实现本接口。

/*** 定时器执行者,定时器都要实现本接口,并需要把实现类加入到spring容器中**/
public interface CommonTimerTaskRunner {/*** 任务执行的具体内容**/void action();
}

2.2、测试-实例

具体执行定时任务的一个类,实现CommonTimerTaskRunner,为了获取该类的类名。

/*** 定时器的一个示例**/
@Slf4j
@Component
public class DevJobTimerTaskRunner implements CommonTimerTaskRunner {private int n = 1;@Overridepublic void action() {log.info("我是一个定时任务,正在在被执行第" + n + "次");n = n + 1;}
}

2.3、定时任务监听器

/*** 定时任务监听器,系统启动时将定时任务启动*/
@Slf4j
@Configuration
public class DevJobListener implements ApplicationListener<ApplicationStartedEvent>, Ordered {@SuppressWarnings("ALL")@Overridepublic void onApplicationEvent( ApplicationStartedEvent applicationStartedEvent) {SpringUtil.getBean(DevJobService.class).list(new LambdaQueryWrapper<DevJob>().eq(DevJob::getJobStatus, DevJobStatusEnum.RUNNING.getValue()).orderByAsc(DevJob::getSortCode)).forEach(devJob -> CronUtil.schedule(devJob.getId(), devJob.getCronExpression(), () -> {try {// 运行定时任务((CommonTimerTaskRunner) SpringUtil.getBean(Class.forName(devJob.getActionClass()))).action();} catch (ClassNotFoundException e) {System.out.println("定时任务找不到对应的类,名称为:" + devJob.getActionClass());}}));// 设置秒级别的启用CronUtil.setMatchSecond(true);// 启动定时器执行器CronUtil.restart();}@Overridepublic int getOrder() {return LOWEST_PRECEDENCE;}
}

2.4、controller类


@Api(tags = "定时任务")
@RestController
public class DevJobController {@AutowiredDevJobService devJobService;/*** 添加定时任务**/@ApiOperation("添加定时任务")@PostMapping("/dev/job/add")public Boolean add(@RequestBody @Valid DevJob devJob) {return devJobService.add(devJob);}/*** 获取定时任务列表*/@ApiOperation("获取定时任务列表")@GetMapping("/dev/job/list")public List<DevJob> list(DevJob devJob) {return devJobService.getlist(devJob);}/*** 获取定时任务类**/@ApiOperation("获取定时任务类")@GetMapping("/dev/job/getActionClass")public List<String> getActionClass() {return devJobService.getActionClass();}
}

2.4、service类

public interface DevJobService extends IService<DevJob> {/*** 获取定时任务列表*/List<DevJob> getlist(DevJob devJob);/*** 添加定时任务*/Boolean add(DevJob devJob);/*** 编辑定时任务*/void edit(DevJob devJob);/*** 删除定时任务**/void delete(List<DevJob> devJobList);/*** 停止定时任务**/void stopJob(DevJob devJob);/*** 运行定时任务**/void runJob(DevJob devJob);/*** 立即运行定时任务***/void runJobNow(DevJob devJob);/*** 获取定时任务类名***/List<String> getActionClass();
}

2.4、serviceImpl类


@Service
public class DevJobServiceImpl extends ServiceImpl<DevJobMapper, DevJob>implements DevJobService{@Overridepublic Boolean add(DevJob devJob) {checkParam(devJob);devJob.setCode(RandomUtil.randomString(10));devJob.setJobStatus(DevJobStatusEnum.STOPPED.getValue());return this.save(devJob);}@Overridepublic void edit(DevJob devJobEditParam) {DevJob devJob = this.getById(devJobEditParam.getId());if(devJob.getJobStatus().equals(DevJobStatusEnum.RUNNING.getValue())) {throw new RuntimeException("运行中的定时任务不可编辑,id值为:");}checkParam(devJobEditParam);BeanUtil.copyProperties(devJobEditParam, devJob);this.updateById(devJob);}@Transactional(rollbackFor = Exception.class)@Overridepublic void delete(List<DevJob> devJobIdParamList) {List<String> devJobIdList = CollStreamUtil.toList(devJobIdParamList, DevJob::getId);if(ObjectUtil.isNotEmpty(devJobIdList)) {// 将运行中的停止devJobIdList.forEach(CronUtil::remove);// 执行删除this.removeByIds(devJobIdList);}}@Transactional(rollbackFor = Exception.class)@Overridepublic void stopJob(DevJob devJobIdParam) {DevJob devJob = this.getById(devJobIdParam.getId());if(devJob.getJobStatus().equals(DevJobStatusEnum.STOPPED.getValue())) {throw new RuntimeException("定时任务已经处于停止状态,id值为:");}// 将运行中的定时任务停止CronUtil.remove(devJob.getId());this.update(new LambdaUpdateWrapper<DevJob>().eq(DevJob::getId, devJobIdParam.getId()).set(DevJob::getJobStatus, DevJobStatusEnum.STOPPED.getValue()));}@Transactional(rollbackFor = Exception.class)@Overridepublic void runJob(DevJob devJobIdParam) {DevJob devJob = this.getById(devJobIdParam.getId());if(devJob.getJobStatus().equals(DevJobStatusEnum.RUNNING.getValue())) {throw new RuntimeException("定时任务已经处于运行状态,id值为");}CronUtil.schedule(devJob.getId(), devJob.getCronExpression(), () -> {try {// 运行定时任务((CommonTimerTaskRunner) SpringUtil.getBean(Class.forName(devJob.getActionClass()))).action();} catch (ClassNotFoundException e) {throw new RuntimeException("定时任务找不到对应的类,名称");}});this.update(new LambdaUpdateWrapper<DevJob>().eq(DevJob::getId, devJobIdParam.getId()).set(DevJob::getJobStatus, DevJobStatusEnum.RUNNING.getValue()));}@Overridepublic void runJobNow(DevJob devJobIdParam) {DevJob devJob = this.getById(devJobIdParam.getId());if(devJob.getJobStatus().equals(DevJobStatusEnum.STOPPED.getValue())) {// 如果是停止的,则先开启运行this.runJob(devJobIdParam);}try {// 直接运行一次((CommonTimerTaskRunner) SpringUtil.getBean(Class.forName(devJob.getActionClass()))).action();} catch (ClassNotFoundException e) {throw new RuntimeException("定时任务找不到对应的类,名称为");}}private void checkParam(DevJob devJobEditParam) {if(!CronExpression.isValidExpression(devJobEditParam.getCronExpression())) {//throw new RuntimeException(("cron表达式:{}格式不正确", devJobEditParam.getCronExpression()));throw new RuntimeException(("cron表达式格式不正确"));}try {Class<?> actionClass = Class.forName(devJobEditParam.getActionClass());if(!CommonTimerTaskRunner.class.isAssignableFrom(actionClass)) {List<String> actionClassArr = StrUtil.split(devJobEditParam.getActionClass(), StrUtil.DOT);//throw new CommonException("定时任务对应的类:{}不符合要求", actionClassArr.get(actionClassArr.size() - 1));throw new RuntimeException("定时任务对应的类不符合要求");}} catch (ClassNotFoundException e) {//throw new CommonException("定时任务找不到对应的类,名称为:{}", devJobEditParam.getActionClass());throw new RuntimeException("定时任务找不到对应的类名称");}boolean hasSameJob = this.count(new LambdaQueryWrapper<DevJob>().eq(DevJob::getActionClass, devJobEditParam.getActionClass()).eq(DevJob::getCronExpression, devJobEditParam.getCronExpression()).ne(DevJob::getId, devJobEditParam.getId())) > 0;if (hasSameJob) {//throw new CommonException("存在重复的定时任务,名称为:{}", devJobEditParam.getName());throw new RuntimeException("存在重复的定时任务");}}@Overridepublic List<DevJob> getlist(DevJob devJob) {return this.list();}@Overridepublic List<String> getActionClass() {Map<String, CommonTimerTaskRunner> commonTimerTaskRunnerMap = SpringUtil.getBeansOfType(CommonTimerTaskRunner.class);if (ObjectUtil.isNotEmpty(commonTimerTaskRunnerMap)) {Collection<CommonTimerTaskRunner> values = commonTimerTaskRunnerMap.values();return values.stream().map(commonTimerTaskRunner -> commonTimerTaskRunner.getClass().getName()).collect(Collectors.toList());} else {return CollectionUtil.newArrayList();}}
}

2.5、状态枚举类和实体类

/*** 定时任务状态枚举*/
@Getter
public enum DevJobStatusEnum {/*** 运行*/RUNNING("RUNNING"),/*** 停止*/STOPPED("STOPPED");private final String value;DevJobStatusEnum(String value) {this.value = value;}public static void validate(String value) {boolean flag = RUNNING.getValue().equals(value) || STOPPED.getValue().equals(value);if(!flag) {throw new RuntimeException("不支持的定时任务状态");}}
}
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;import java.io.Serializable;
import java.util.Date;import lombok.Data;/*** 定时任务** @TableName dev_job*/
@TableName(value = "dev_job")
@Data
public class DevJob implements Serializable {/****/@TableIdprivate String id;/*** 名称*/private String name;/*** 编码*/private String code;/*** 分类*/private String category;/*** 任务类名*/private String actionClass;/*** cron表达式*/private String cronExpression;/*** 任务状态*/private String jobStatus;/*** 排序码*/private Integer sortCode;/*** 扩展信息*/private String extJson;/*** 删除标志*/private String deleteFlag;/*** 创建时间*/private Date createTime;/*** 创建用户*/private String createUser;/*** 修改时间*/private Date updateTime;/*** 修改用户*/private String updateUser;@TableField(exist = false)private static final long serialVersionUID = 1L;
}

本文链接:https://my.lmcjl.com/post/8242.html

展开阅读全文

4 评论

留下您的评论.