SpringBoot实现动态增改启停定时任务

有时系统中需要定时任务做别的事情,但是简单的定时任务是无法人为去控制的。
在SpringBoot中可以通过@EnableScheduling注解和@Scheduled注解实现定时任务,也可以通过SchedulingConfigurer接口来实现定时任务。但是这两种方式不能动态添加、删除、启动、停止任务。
要实现上面的需求,一般来说可以使用框架——Quartz框架。
下面要说的就是不去依赖别的定时任务框架实现需求。

本篇博客所分享知识非本人原创,参考某一日在微信看到的一篇公众号发的文章,目前找不到了。

添加执行定时任务的线程池配置类
package com.likegakki.springbootschedul.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;

/**
 * date: 2021/7/24 16:14
 *
 * @author LOVEGAKKI
 * Description: 添加执行定时任务的线程池配置类
 */
@Configuration
public class SchedulingConfig {


    @Bean
    public TaskScheduler taskScheduler(){
        ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
        //定时任务执行线程池核心数
        taskScheduler.setPoolSize(4);
        taskScheduler.setRemoveOnCancelPolicy(true);
        taskScheduler.setThreadNamePrefix("ThreadPoolTaskScheduler-");
        return taskScheduler;
    }
}
添加ScheduledFuture的包装类。ScheduledFuture是ScheduledExecutorService定时任务线程池的执行结果。
package com.likegakki.springbootschedul.scheduler;

import java.util.concurrent.ScheduledFuture;

/**
 * date: 2021/7/24 16:17
 *
 * @author LOVEGAKKI
 * Description:
 */

public final class ScheduledTask {

    volatile ScheduledFuture<?> future;

    /**
     * 取消定时任务
     */
    public void cancel(){
        ScheduledFuture<?> future = this.future;
        if (future != null){
            future.cancel(true);
        }
    }
}
添加Runnable接口实现类,被定时任务线程池调用,用来执行指定bean里面的方法。
package com.likegakki.springbootschedul.scheduler;

import com.likegakki.springbootschedul.util.SpringContextUtils;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;

import java.lang.reflect.Method;

/**
 * date: 2021/7/24 16:23
 *
 * @author LOVEGAKKI
 * Description: 添加Runnable接口实现类,被定时任务线程池调用,用来执行指定bean里面的方法。
 */
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode
public class SchedulingRunnable implements Runnable{


    private static final Logger logger = LoggerFactory.getLogger(SchedulingRunnable.class);

    private String beanName;

    private String methodName;

    private String params;

    public SchedulingRunnable(String beanName,String methodName){
        this(beanName,methodName,null);
    }

    @Override
    public void run() {
        logger.info("定时任务开始执行 - bean:{},方法:{},参数:{}", beanName, methodName, params);
        long startTime = System.currentTimeMillis();

        try{
            Object target = SpringContextUtils.getBean(beanName);

            Method method= null;

            if (!StringUtils.isEmpty(params)){
                method = target.getClass().getDeclaredMethod(methodName,String.class);
            }else {
                method = target.getClass().getDeclaredMethod(methodName);
            }

            ReflectionUtils.makeAccessible(method);

            if (!StringUtils.isEmpty(params)){
                method.invoke(target,params);
            }else {
                method.invoke(target);
            }
        }catch (Exception e){
            logger.error(String.format("定时任务执行异常 - bean:%s,方法:%s,参数:%s ", beanName, methodName, params), e);
        }
        long times = System.currentTimeMillis() - startTime;
        logger.info("定时任务执行结束 - bean:{},方法:{},参数:{},耗时:{} 毫秒", beanName, methodName, params, times);
    }
}

@NoArgsConstructor,@AllArgsConstructor,@EqualsAndHashCode
这三个注解是来自Lombok,分别是:无参构造,全参构造,重写Equals和HashCode方法。
这里使用的SpringContextUtils代码如下:

package com.likegakki.springbootschedul.util;

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

@Component
public class SpringContextUtils implements ApplicationContextAware {

    private static ApplicationContext applicationContext;

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

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

    public static <T> T getBean(Class<T> requiredType) {
        return applicationContext.getBean(requiredType);
    }

    public static <T> T getBean(String name, Class<T> requiredType) {
        return applicationContext.getBean(name, requiredType);
    }

    public static boolean containsBean(String name) {
        return applicationContext.containsBean(name);
    }

    public static boolean isSingleton(String name) {
        return applicationContext.isSingleton(name);
    }

    public static Class<? extends Object> getType(String name) {
        return applicationContext.getType(name);
    }
}
添加定时任务注册类,用来增加、删除定时任务。
package com.likegakki.springbootschedul.scheduler;

import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.config.CronTask;
import org.springframework.stereotype.Component;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * date: 2021/7/24 18:02
 *
 * @author LOVEGAKKI
 * Description: 添加定时任务注册类,用来增加、删除定时任务。
 */
@Component
public class CronTaskRegistrar implements DisposableBean {

    private final Map<Runnable,ScheduledTask> scheduledTasks = new ConcurrentHashMap<>(16);

    @Autowired
    private TaskScheduler taskScheduler;

    public TaskScheduler getTaskScheduler(){
        return this.taskScheduler;
    }

    public void addCronTask(Runnable task,String cronExpression){
        addCronTask(new CronTask(task,cronExpression));
    }

    public void addCronTask(CronTask cronTask) {
        if (cronTask != null){
            Runnable task = cronTask.getRunnable();
            if (this.scheduledTasks.containsKey(task)){

                removeCronTask(task);
            }

            this.scheduledTasks.put(task,scheduledCronTask(cronTask));
        }
    }

    public void removeCronTask(Runnable task){
        ScheduledTask scheduledTask = this.scheduledTasks.remove(task);
        if (scheduledTask != null){
            scheduledTask.cancel();
        }
    }

    public ScheduledTask scheduledCronTask(CronTask cronTask){
        ScheduledTask scheduledTask = new ScheduledTask();
        scheduledTask.future = this.taskScheduler.schedule(cronTask.getRunnable(),cronTask.getTrigger());

        return scheduledTask;
    }

    @Override
    public void destroy() throws Exception {

        for (ScheduledTask task : this.scheduledTasks.values()){
            task.cancel();
        }

        this.scheduledTasks.clear();
    }
}

编写定时任务代码
package com.likegakki.springbootschedul.scheduler;

import org.springframework.stereotype.Component;

/**
 * date: 2021/7/24 20:11
 *
 * @author LOVEGAKKI
 * Description:
 */
@Component("demoTask")
public class DemoTask {

    public void taskWithParams(String param){
        System.out.println("执行有参示例任务:" + param);
    }

    public void taskNoParams(){
        System.out.println("执行无参示例任务");
    }
}

创建定时任务实体类对象
package com.likegakki.springbootschedul.entity;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

import java.io.Serializable;
import java.util.Date;

/**
 * date: 2021/7/24 20:17
 *
 * @author LOVEGAKKI
 * Description:
 */
@ApiModel("定时任务实体类")
@Data
@TableName("system_job")
public class SystemJobEntity implements Serializable {

    /**
     * 任务ID
     */
    @TableId(type = IdType.AUTO)
    private Integer jobId;
    /**
     * bean名称
     */
    private String beanName;
    /**
     * 方法名称
     */
    private String methodName;
    /**
     * 方法参数
     */
    private String methodParams;
    /**
     * cron表达式
     */
    private String cronExpression;
    /**
     * 状态(1正常 0暂停)
     */
    private Integer jobStatus;
    /**
     * 备注
     */
    private String remark;
    /**
     * 创建时间
     */
    private Date createTime;
    /**
     * 更新时间
     */
    private Date updateTime;
}

定时任务状态枚举类
package com.likegakki.springbootschedul.enums;

public enum SystemJobStatus {

    /**
     * 暂停
     */
    PAUSE,

    /**
     * 正常
     */
    NORMAL
}

对定时任务的增删改查(包含增改启停)
package com.likegakki.springbootschedul.controller;

import com.likegakki.springbootschedul.entity.SystemJobEntity;
import com.likegakki.springbootschedul.enums.SystemJobStatus;
import com.likegakki.springbootschedul.scheduler.CronTaskRegistrar;
import com.likegakki.springbootschedul.scheduler.SchedulingRunnable;
import com.likegakki.springbootschedul.service.SystemJobService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;

/**
 * date: 2021/7/24 20:23
 *
 * @author LOVEGAKKI
 * Description:
 */
@Api(value = "SystemJobController")
@RestController
@RequestMapping("/system/job")
public class SystemJobController {


    @Autowired
    private SystemJobService systemJobService;

    @Autowired
    private CronTaskRegistrar cronTaskRegistrar;

    @ApiOperation("新增定时任务")
    @PostMapping
    public String add(@RequestBody SystemJobEntity systemJobEntity){

        boolean success = systemJobService.save(systemJobEntity);
        if (success){
        	//判断当前创建的任务状态,如果是启动,添加
            if (systemJobEntity.getJobStatus().equals(SystemJobStatus.NORMAL.ordinal())){
                SchedulingRunnable task = new SchedulingRunnable(systemJobEntity.getBeanName(), systemJobEntity.getMethodName(), systemJobEntity.getMethodParams());
                cronTaskRegistrar.addCronTask(task,systemJobEntity.getCronExpression());
            }
            return "ok";
        }
        return "error";
    }

    @ApiOperation("删除定时任务")
    @DeleteMapping("/{jobId}")
    public String delete(@PathVariable String jobId){
        SystemJobEntity systemJobEntity = systemJobService.getById(jobId);
        boolean success = systemJobService.removeById(jobId);
        if (success){
            if (systemJobEntity.getJobStatus().equals(SystemJobStatus.NORMAL.ordinal())){
                SchedulingRunnable task = new SchedulingRunnable(systemJobEntity.getBeanName(), systemJobEntity.getMethodName(), systemJobEntity.getMethodParams());
                cronTaskRegistrar.removeCronTask(task);
            }
            return "ok";
        }
        return "error";
    }

    @ApiOperation("修改定时任务")
    @PutMapping
    public String edit(@RequestBody SystemJobEntity systemJobEntity){
        SystemJobEntity oldSystemJobEntity = systemJobService.getById(systemJobEntity.getJobId());
        boolean success = systemJobService.updateById(systemJobEntity);

        if (success){
            //先移除再添加
            if (oldSystemJobEntity.getJobStatus().equals(SystemJobStatus.NORMAL.ordinal())){
                SchedulingRunnable task = new SchedulingRunnable(oldSystemJobEntity.getBeanName(), oldSystemJobEntity.getMethodName(), oldSystemJobEntity.getMethodParams());
                cronTaskRegistrar.removeCronTask(task);
            }

            if (systemJobEntity.getJobStatus().equals(SystemJobStatus.NORMAL.ordinal())){
                SchedulingRunnable task = new SchedulingRunnable(systemJobEntity.getBeanName(), systemJobEntity.getMethodName(), systemJobEntity.getMethodParams());
                cronTaskRegistrar.addCronTask(task,systemJobEntity.getCronExpression());
            }
            return "ok";
        }
        return "error";
    }

    @ApiOperation("获取所有定时任务")
    @GetMapping
    public List<SystemJobEntity> list(){
        return systemJobService.list();
    }
}

定时任务的代码肯定是提前写好的,做不到动态增加定时任务业务代码。

代码地址:https://gitee.com/likegakki/springboot-schedul

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Boot通过@EnableScheduling注解来开启定时任务的功能。下面是实现动态定时任务的步骤: 1. 首先,在你的Spring Boot应用的主类上添加@EnableScheduling注解,启用定时任务的支持。例如: ```java import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.scheduling.annotation.EnableScheduling; @SpringBootApplication @EnableScheduling public class YourApplication { public static void main(String[] args) { SpringApplication.run(YourApplication.class, args); } } ``` 2. 接下来,在需要执行定时任务的方法上添加@Scheduled注解,并设置定时任务的执行规则。例如,我们可以使用cron表达式来定义定时任务的执行时间。以下是一个示例: ```java import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; @Component public class MyScheduledTask { @Scheduled(cron = "0 0/5 * * * *") // 每5分钟执行一次 public void doSomething() { // 定时任务要执行的操作 } } ``` 3. 现在,当应用启动后,定时任务会按照定义的规则自动执行。 如果你想在运行时动态修改定时任务的执行规则,可以借助Spring的ScheduledTaskRegistrar类。可以在应用程序中注入ScheduledTaskRegistrar对象,然后使用其方法来注册、取消和修改定时任务。这样你就可以实现动态定时任务调度了。 希望这个回答对你有帮助!如果你还有其他问题,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值