springboot定时任务动态启停

49 阅读2分钟
package com.demo.autotask;/*
 * Copyright 2002-2022 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


import org.springframework.lang.Nullable;
import org.springframework.scheduling.config.*;

import java.util.concurrent.ScheduledFuture;

/**
 * A representation of a scheduled task at runtime,
 * used as a return value for scheduling methods.
 *
 * @author Juergen Hoeller
 * @see ScheduledTaskRegistrar#scheduleCronTask(CronTask)
 * @see ScheduledTaskRegistrar#scheduleFixedRateTask(FixedRateTask)
 * @see ScheduledTaskRegistrar#scheduleFixedDelayTask(FixedDelayTask)
 * @see ScheduledFuture
 * @since 4.3
 */
public final class CustomScheduledTask {

    private final Task task;

    @Nullable
    volatile ScheduledFuture<?> future;


    CustomScheduledTask(Task task) {
        this.task = task;
    }


    /**
     * Return the underlying task (typically a {@link CronTask},
     * {@link FixedRateTask} or {@link FixedDelayTask}).
     *
     * @since 5.0.2
     */
    public Task getTask() {
        return this.task;
    }

    /**
     * Trigger cancellation of this scheduled task.
     * <p>This variant will force interruption of the task if still running.
     *
     * @see #cancel(boolean)
     */
    public void cancel() {
        cancel(true);
    }

    /**
     * Trigger cancellation of this scheduled task.
     *
     * @param mayInterruptIfRunning whether to force interruption of the task
     *                              if still running (specify {@code false} to allow the task to complete)
     * @see ScheduledFuture#cancel(boolean)
     * @since 5.3.18
     */
    public void cancel(boolean mayInterruptIfRunning) {
        ScheduledFuture<?> future = this.future;
        if (future != null) {
            future.cancel(mayInterruptIfRunning);
        }
    }

    @Override
    public String toString() {
        return this.task.toString();
    }

}
package com.demo.autotask;

import cn.hutool.core.util.StrUtil;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.Trigger;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.scheduling.config.TriggerTask;

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

/**
 * @description: 自定义定时任务注册器
 * 本类请参考 {@link ScheduledTaskRegistrar},通过该类进行自定义即可
 */
@Slf4j
@AllArgsConstructor
@Configuration
public class CustomScheduledTaskRegistrar implements DisposableBean {

    private TaskScheduler taskScheduler;

    private final Map<String, CustomScheduledTask> scheduledTasks = new ConcurrentHashMap(16);


    public void addTriggerTask(String key, Runnable task, Trigger trigger) {
        if (task == null || trigger == null || StrUtil.isBlank(key)) {
            log.error("任务key和任务线程以及触发器不能为空");
            return;
        }
        CustomScheduledTask customScheduledTask1 = scheduledTasks.get(key);
        if (customScheduledTask1 != null) {
            log.error("{}---对应的任务已存在,请勿重复创建,如需重复创建,请先执行删除后在尝试新建任务", key);
            return;
        }
        // addTriggerTask(new TriggerTask(task, trigger));
        CustomScheduledTask customScheduledTask = scheduleTriggerTask(new TriggerTask(task, trigger));
        customScheduledTask.future = this.taskScheduler.schedule(task, trigger);
        scheduledTasks.put(key, customScheduledTask);
    }

    public void removeTriggerTask(String key) {
        if (StrUtil.isBlank(key)) {
            log.error("key不能为空");
            return;
        }
        CustomScheduledTask scheduledTask = scheduledTasks.get(key);
        if (scheduledTask == null) {
            log.error("{}对应的任务不存在,请勿重复删除", key);
        } else {
            scheduledTask.cancel();
            scheduledTasks.remove(key);
        }
    }


    private CustomScheduledTask scheduleTriggerTask(TriggerTask task) {

        return new CustomScheduledTask(task);
    }


    @Override
    public void destroy() throws Exception {
        for (CustomScheduledTask task : this.scheduledTasks.values()) {
            task.cancel();
        }
    }
}
package com.demo.autotask;

import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;
import org.springframework.scheduling.config.CronTask;
import org.springframework.scheduling.support.CronTrigger;

/**
 * @author none
 */
@Accessors(chain = true)
@Getter
@Setter
public class CustomTask extends CronTask {

    private String taskName;

    private int taskSchedule;

    private int status;

    private String taskFunc;

    private String cron = "0/1 * * * * ?";


    public CustomTask(Runnable runnable, String expression) {
        super(runnable, expression);
    }

    public CustomTask(Runnable runnable, CronTrigger cronTrigger) {
        super(runnable, cronTrigger);
    }
}
package com.demo.autotask;

import lombok.Getter;
import lombok.Setter;
import org.springframework.stereotype.Component;

/**
 * @author none
 */
@Component
@Getter
@Setter
public class TaskAPI {

    public void execTask1() {
        System.out.println("exec task1");
    }

    public void execTask2() {
        System.out.println("exec task2");
    }
}
package com.demo.autotask;

import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

/**
 * @author none
 */
@Component
public class TaskAPIManager {

    @Resource
    private TaskAPI taskAPI;

    @Resource
    private CustomScheduledTaskRegistrar customScheduledTaskRegistrar;

    public List<CustomTask> getTasks() {
        Class clz = taskAPI.getClass();
        List<CustomTask> customTasks = new ArrayList<>();
        for (Method method : clz.getDeclaredMethods()) {
            method.setAccessible(true);
            customTasks.add(
                    new CustomTask(() -> {
                        try {
                            method.invoke(taskAPI);
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        } catch (InvocationTargetException e) {
                            e.printStackTrace();
                        }
                    }, "0/1 * * * * ?").setTaskName(method.getName()).setStatus(1)
                            .setTaskFunc(method.getName())
            );
        }
        return customTasks;
    }

    public void add(CustomTask customTask) {
        customScheduledTaskRegistrar.addTriggerTask(customTask.getTaskName(), customTask.getRunnable(), triggerContext -> {
            return new CronTrigger(customTask.getCron()).nextExecutionTime(triggerContext);
        });
    }


    public void remove(String taskName) {
        customScheduledTaskRegistrar.removeTriggerTask(taskName);
    }
}
package com.demo.autotask;

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
@RequestMapping("/api")
@Slf4j
public class TaskController {
    @Resource
    private TaskAPIManager taskAPIManager;

    @RequestMapping("/task/exec")
    public String exec(String taskName) {
        for (CustomTask customTask : taskAPIManager.getTasks()) {
            if (taskName.equals(customTask.getTaskName())) {
                taskAPIManager.add(customTask);
            }
        }
        return "exec success";
    }

    @RequestMapping("/task/remove")
    public String remove(String taskName) {
        taskAPIManager.remove(taskName);
        return "remove success";
    }
}