学无先后,达者为师

网站首页 编程语言 正文

spring实现定时任务的动态可配置、可删除、可启用停用功能

作者:街角等待 更新时间: 2024-07-13 编程语言

一般情况下定时任务的使用常见的两个组件:xxl-job和quartz,这两种需要引用相应的依赖,针对对定时任务较为复杂和依赖性较高的系统
但是如果项目中使用定时任务的场景不多,避免项目过于臃肿和复杂,但要求可配置、可删除、可启停用的

定时任务的必要字段

TASK_NAME: 定时任务名称;
TASK_CRON:定时任务执行频率,cron表达式
TASK_BEAN: 执行定时任务的Bean名称
TASK_MOTHOD: 执行定时任务的方法名

一、创建线程池并设置参数

ThreadPoolTaskScheduler:

主要用于任务调度,可以按照时间间隔或Cron表达式来执行任务。
继承自TaskScheduler接口,并实现了AsyncTaskExecutor接口。
它基于线程池来执行任务,并且可以根据需要调度任务的执行。

特点:
强调任务调度和时间控制。
适用于需要按照特定时间间隔或规则执行任务的场景。

其他线程:

主要用于执行异步任务和多线程任务。
专注于线程池的管理和配置,如线程池的大小、队列容量以及线程的创建和销毁策略

特点:
强调线程池的管理和异步任务的执行。
适用于需要并发执行大量异步任务的场景。

@Configuration
public class SchedulingConfig {
    @Bean
    public TaskScheduler taskScheduler() {
        ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
        
        taskScheduler.setPoolSize(4);
        taskScheduler.setRemoveOnCancelPolicy(true);
        taskScheduler.setThreadNamePrefix("TaskSchedulerThreadPool-");
        return taskScheduler;
    }
}

二、添加 ScheduledFuture 的包装类

ScheduledTaskFuture :
主要用于处理和控制定时任务的执行结果和执行状态。它提供了异步执行结果的获取、任务执行状态的控制、任务执行结果的获取和异常处理、任务的取消以及超时获取任务结果等功能,使得你可以更加方便地管理和控制定时任务的执行。

1、可以通过ScheduledTaskFuture来获取定时任务执行后的结果,查询定时任务是否已完成(isDone()方法)、是否已被取消(isCancelled()方法)或者是否正在执行(!isDone() && !isCancelled())。
2、可以调用ScheduledTaskFuture的get()方法来获取定时任务执行的结果。
3、可以调用cancel(boolean mayInterruptIfRunning)方法来取消定时任务的执行。如果参数mayInterruptIfRunning为true,尝试中断正在执行的任务;如果为false,则正在执行的任务不会被中断,但是等待执行的任务会被取消

@Component
public final class ScheduledTaskFuture {

    public volatile ScheduledFuture<?> scheduledFuture;

    public void cancel(){
        ScheduledFuture<?> future = this.scheduledFuture;
        if (future != null) {
            future.cancel(true);
        }
    }

}

三、定时任务实体类

public class DismTimedTask {

    /**
     * 任务ID
     */
    @Id
    @Column(name = "TASK_ID")
    private Long taskId;

    /**
     * 任务名称
     */
    @Column(name = "TASK_NAME")
    private String taskName;

    /**
     * 任务执行频率
     */
    @Column(name = "TASK_CRON")
    private String taskCron;

    /**
     * 时间范围
     */
    @Column(name = "TASK_BEAN")
    private int taskBean;

    /**
     * 是否包含
     */
    @Column(name = "TASK_METHOD_NAME")
    private String taskMethodName;
    
    /**
     * 模板状态
     */
    @Column(name = "STATUS")
    private String status;
}

四、任务的执行

public class SchedulingRunnable implements Runnable{
	/**
	* 执行定时任务的Bean名称
	*/
    private String beanName;
	/**
	* 执行定时任务的方法名称
	*/
    private String methodName;
	/**
	* 定时任务的参数、可定义为其他类型
	*/
    private Long taskId;

    public SchedulingRunnable(String beanName, String methodName, Long taskId) {
        this.beanName = beanName;
        this.methodName = methodName;
        this.taskId = taskId;
    }


    @Override
    public void run() {
        log.info("定时任务开始执行 - bean: {}, 方法: {}", beanName, methodName);
        try {
            Object target = SpringContextUtils.getBean(beanName);

            if (taskId == null || taskId == 0L) {
            // 无参的
                Method method = target.getClass().getMethod(methodName);
                method.invoke(target);
            } else {
            //有参的
                Method method = target.getClass().getMethod(methodName, Long.class);
                method.invoke(target, taskId);
            }
        } catch (Exception ex) {
            log.error(String.format("定时任务执行异常-bean: %s, 方法: %s", beanName, methodName));
            log.error("异常:" + ex);
        }
        log.info("定时任务执行结束 - bean: {}, 方法: {}", beanName, methodName);
    }
}

五、定时任务的创建、删除操作

@Service
@Slf4j
public class TimedTaskServiceImpl implements TimedTaskService {

    private final Map<Long, ScheduledTaskFuture> scheduledTasks = new ConcurrentHashMap<>(16);

    @Resource
    private TaskScheduler taskScheduler;
    /**
     * 更新定时任务
     * @author Liyh
     * @date 2024/06/17 09:56
     * @param timedTask
     */
    @Transactional(rollbackFor = Exception.class)
    public void updateTimedTask(TimedTask timedTask) {
    //定时任务执行Bean
        SchedulingRunnable schedulingRunnable = new SchedulingRunnable(timedTask.getBean(), timedTask.getMothod(), timedTask.getTaskId());
        CronTask cronTask = new CronTask(schedulingRunnable, timedTask.getTaskCron());
      
        if ("1".equals(timedTask.getStatus())) {
        // 启用
        // 如果定时任务已经存在,先删后增,
            if (this.scheduledTasks.containsKey(timedTask.getTaskId())){
                removeCronTask(timedTask.getTaskId());
            }
            this.scheduledTasks.put(timedTask.getTaskId(), scheduledTask(cronTask));
        } else {
        // 停用
            removeCronTask(timedTask.getTaskId());
        }
    }

    /**
     * 删除定时任务
     * @author Liyh
     * @date 2024/06/11 17:41
     * @param taskId
     */
    public void removeCronTask(Long taskId){
        ScheduledTaskFuture scheduledTask = this.scheduledTasks.remove(taskId);
        if (scheduledTask != null) {
            scheduledTask.cancel();
        }
    }

    /**
     * 创建定时任务
     * @author Liyh
     * @date 2024/06/11 17:41
     * @param cronTask
     * @return ScheduledTaskFuture
     */
    public ScheduledTaskFuture scheduledTask(CronTask cronTask){

        ScheduledTaskFuture scheduledTask = new ScheduledTaskFuture();

        scheduledTask.scheduledFuture = this.taskScheduler.schedule(cronTask.getRunnable(), cronTask.getTrigger());

        return scheduledTask;
    }
}
/*删除任务时执行removeCronTask方法即可*/

六、获取Bean的工具类

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

}

7、程序启动时加载数据库中启用的任务

@Component
public class TimedTaskRunner implements CommandLineRunner {
    @Resource
    private TimedTaskService timedTaskService;

    @Override
    public void run(String... args) throws Exception {
        timedTaskService.runAllTimedTask();
    }
}



具体实现类方法
public void runAllTimedTask() {
        List<DismTimedTask> timedTaskList = timedTaskDao.getDismTimedTaskByStatus("1");
        if (timedTaskList != null && !timedTaskList.isEmpty()) {
            for (DismTimedTask timedTask : timedTaskList) {
                SchedulingRunnable schedulingRunnable = new SchedulingRunnable(null, "taskRunning", timedTask.getTaskId());
                CronTask cronTask = new CronTask(schedulingRunnable, timedTask.getTaskCron());
                scheduledTask(cronTask);
                this.scheduledTasks.put(timedTask.getTaskId(), scheduledTask(cronTask));
                log.info("定时任务已加载完毕...");
            }
        }
    }

原文链接:https://blog.csdn.net/saonian0929/article/details/139850453

  • 上一篇:没有了
  • 下一篇:没有了
栏目分类
最近更新