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

文章描述了一个使用JavaSpring实现的定时任务管理系统。通过创建数据库表`dev_job`存储任务信息,包括任务类名和cron表达式。定义了`CommonTimerTaskRunner`接口供任务类实现,并用`DevJobListener`监听器在应用启动时加载运行中的任务。提供了添加、删除、编辑和运行定时任务的Controller及Service接口。
摘要由CSDN通过智能技术生成

思路:首先是能够获取任务具体执行的类名,然后将任务类名以及cron表达式新增到数据库保存起来,有一个监听类,根据任务类名和cron表达式用来运行该定时任务。
具体来说:

  • 项目一启动,就要去查询定时任务表,并把数据库运行状态的任务加到监听表,监听执行,具体在 2.3,定时任务监听器;
  • 然后就是2.4是实现具体的操作,比如:增删改查定时任务、直接执行一次定时任务。

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='定时任务';
INSERT INTO `mybatisplus`.`dev_job`(`ID`, `NAME`, `CODE`, `CATEGORY`, `ACTION_CLASS`, `CRON_EXPRESSION`, `JOB_STATUS`, `SORT_CODE`, `EXT_JSON`, `DELETE_FLAG`, `CREATE_TIME`, `CREATE_USER`, `UPDATE_TIME`, `UPDATE_USER`) VALUES ('1648226556708933634', '定时任务1', NULL, NULL, 'com.lu.mybatisdemo.job.task.DevJobTimerTaskRunner', '*/5 * * * * ?', 'RUNNING', 0, NULL, NULL, '2023-04-18 15:24:49', NULL, NULL, NULL);

2、实现代码

2.1、定时器执行者接口

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

/**
 * 定时器执行者,定时器都要实现本接口,并需要把实现类加入到spring容器中
 **/
public interface CommonTimerTaskRunner {

    /**
     * 任务执行的具体内容
     **/
    void action();
}

2.2、测试-实例

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

/**
 * 定时器的一个示例
 **/
@Slf4j
@Component
public class DevJobTimerTaskRunner implements CommonTimerTaskRunner {

    private int n = 1;

    @Override
    public void action() {
        log.info("我是一个定时任务,正在在被执行第" + n + "次");
        n = n + 1;
    }
}

2.3、定时任务监听器

查询数据库中的定时任务,放进去等待时间执行。

/**
 * 定时任务监听器,系统启动时将定时任务启动
 */
@Slf4j
@Configuration
public class DevJobListener implements ApplicationListener<ApplicationStartedEvent>, Ordered {

    @SuppressWarnings("ALL")
    @Override
    public 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();
    }

    @Override
    public int getOrder() {
        return LOWEST_PRECEDENCE;
    }
}

2.4、controller类


@Api(tags = "定时任务")
@RestController
public class DevJobController {

    @Autowired
    DevJobService 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{


    @Override
    public Boolean add(DevJob devJob) {
        checkParam(devJob);
        devJob.setCode(RandomUtil.randomString(10));
        devJob.setJobStatus(DevJobStatusEnum.STOPPED.getValue());
        return this.save(devJob);
    }

    @Override
    public 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)
    @Override
    public 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)
    @Override
    public 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)
    @Override
    public 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()));
    }

    @Override
    public 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("存在重复的定时任务");
        }
    }

    @Override
    public List<DevJob> getlist(DevJob devJob) {
        return this.list();
    }

    @Override
    public 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 {
    /**
     *
     */
    @TableId
    private 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;
}
  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
SpringBoot是一个非常流行的Java Web框架,而mybatis-plus是一个非常强大的ORM框架,两者合起来可以实现非常好用的定时任务管理。 下面是基于SpringBoot和mybatis-plus实现定时任务管理的步骤: 1. 添加依赖 在pom.xml文件中添加以下依赖: ```xml <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.4.3.1</version> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-generator</artifactId> <version>3.4.3.1</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-quartz</artifactId> </dependency> </dependencies> ``` 2. 配置数据库 在application.properties文件中添加以下配置: ``` # 数据库配置 spring.datasource.url=jdbc:mysql://localhost:3306/mydb?useUnicode=true&characterEncoding=utf-8&useSSL=false spring.datasource.username=root spring.datasource.password=123456 spring.datasource.driver-class-name=com.mysql.jdbc.Driver # mybatis-plus配置 mybatis-plus.mapper-locations=classpath:/mapper/*.xml # Quartz配置 org.quartz.scheduler.instanceName = QuartzScheduler org.quartz.scheduler.instanceId = AUTO org.quartz.threadPool.threadCount=10 org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate org.quartz.jobStore.dataSource=myDS org.quartz.jobStore.tablePrefix=QRTZ_ org.quartz.dataSource.myDS.driver=com.mysql.jdbc.Driver org.quartz.dataSource.myDS.URL=jdbc:mysql://localhost:3306/mydb?useUnicode=true&characterEncoding=utf-8&useSSL=false org.quartz.dataSource.myDS.user=root org.quartz.dataSource.myDS.password=123456 org.quartz.dataSource.myDS.maxConnections=5 ``` 3. 创建实体类和Mapper 创建一个定时任务的实体类,并在其中定义定时任务的属性: ```java @Data public class JobEntity { /** * 任务ID */ private String jobId; /** * 任务名称 */ private String jobName; /** * 任务分组 */ private String jobGroup; /** * 任务执行类 */ private String jobClass; /** * 任务描述 */ private String jobDescription; /** * 任务状态 0禁用 1启用 */ private Integer jobStatus; /** * 任务表达式 */ private String jobExpression; /** * 创建时间 */ private Date createTime; /** * 修改时间 */ private Date modifyTime; } ``` 创建一个定时任务的Mapper,并继承mybatis-plus的BaseMapper: ```java @Repository public interface JobMapper extends BaseMapper<JobEntity> { } ``` 4. 创建定时任务工具类 创建一个定时任务工具类,用于实现定时任务的添加、删除、暂停和恢复等操作: ```java @Service public class QuartzService { @Autowired private Scheduler scheduler; /** * 添加任务 * * @param jobEntity */ public void addJob(JobEntity jobEntity) throws SchedulerException, ClassNotFoundException { // 获取任务类 Class<? extends Job> jobClass = (Class<? extends Job>) Class.forName(jobEntity.getJobClass()); // 创建任务 JobDetail jobDetail = JobBuilder.newJob(jobClass) .withIdentity(jobEntity.getJobName(), jobEntity.getJobGroup()) .withDescription(jobEntity.getJobDescription()) .storeDurably(true) .build(); // 创建触发器 Trigger trigger = TriggerBuilder.newTrigger() .withIdentity(jobEntity.getJobName(), jobEntity.getJobGroup()) .withSchedule(CronScheduleBuilder.cronSchedule(jobEntity.getJobExpression())) .build(); // 将任务和触发器添加到调度器 scheduler.scheduleJob(jobDetail, trigger); } /** * 删除任务 * * @param jobId * @param jobGroup */ public void deleteJob(String jobId, String jobGroup) throws SchedulerException { JobKey jobKey = new JobKey(jobId, jobGroup); scheduler.deleteJob(jobKey); } /** * 暂停任务 * * @param jobId * @param jobGroup */ public void pauseJob(String jobId, String jobGroup) throws SchedulerException { JobKey jobKey = new JobKey(jobId, jobGroup); scheduler.pauseJob(jobKey); } /** * 恢复任务 * * @param jobId * @param jobGroup */ public void resumeJob(String jobId, String jobGroup) throws SchedulerException { JobKey jobKey = new JobKey(jobId, jobGroup); scheduler.resumeJob(jobKey); } } ``` 5. 创建定时任务控制器 创建一个定时任务的控制器,用于实现定时任务的增删改查等操作: ```java @RestController @RequestMapping("/job") public class JobController { @Autowired private JobMapper jobMapper; @Autowired private QuartzService quartzService; /** * 添加任务 * * @param jobEntity */ @PostMapping("/add") public void addJob(@RequestBody JobEntity jobEntity) throws SchedulerException { // 添加任务 quartzService.addJob(jobEntity); // 保存任务到数据库 jobMapper.insert(jobEntity); } /** * 删除任务 * * @param jobId * @param jobGroup */ @DeleteMapping("/delete") public void deleteJob(String jobId, String jobGroup) throws SchedulerException { // 删除任务 quartzService.deleteJob(jobId, jobGroup); // 从数据库中删除任务 QueryWrapper<JobEntity> queryWrapper = new QueryWrapper<>(); queryWrapper.eq("job_id", jobId); queryWrapper.eq("job_group", jobGroup); jobMapper.delete(queryWrapper); } /** * 暂停任务 * * @param jobId * @param jobGroup */ @PutMapping("/pause") public void pauseJob(String jobId, String jobGroup) throws SchedulerException { // 暂停任务 quartzService.pauseJob(jobId, jobGroup); // 更新任务状态 JobEntity jobEntity = new JobEntity(); jobEntity.setJobId(jobId); jobEntity.setJobGroup(jobGroup); jobEntity.setJobStatus(0); jobMapper.updateById(jobEntity); } /** * 恢复任务 * * @param jobId * @param jobGroup */ @PutMapping("/resume") public void resumeJob(String jobId, String jobGroup) throws SchedulerException { // 恢复任务 quartzService.resumeJob(jobId, jobGroup); // 更新任务状态 JobEntity jobEntity = new JobEntity(); jobEntity.setJobId(jobId); jobEntity.setJobGroup(jobGroup); jobEntity.setJobStatus(1); jobMapper.updateById(jobEntity); } /** * 获取任务列表 * * @return */ @GetMapping("/list") public List<JobEntity> listJob() { return jobMapper.selectList(null); } } ``` 至此,基于SpringBoot和mybatis-plus实现定时任务管理的步骤就完成了。我们可以通过访问控制器提供的接口来实现定时任务的添加、删除、暂停和恢复等操作。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值