当前位置: 代码网 > it编程>编程语言>Java > springboot使用Scheduling实现动态增删启停定时任务教程

springboot使用Scheduling实现动态增删启停定时任务教程

2025年04月27日 Java 我要评论
在项目开发过程中,如果是一些简单的工程,非分布式工程,一般我们可以使用@enablescheduling注解和@scheduled注解实现简单的定时任务,也可以使用schedulingconfigur

在项目开发过程中,如果是一些简单的工程,非分布式工程,一般我们可以使用@enablescheduling注解和@scheduled注解实现简单的定时任务,也可以使用schedulingconfigurer接口来实现定时任务。那如何动态的来生成定时任务呢?

下面是具体步骤,可以结合数据库,来存储定时任务所需要的参数数据,如bean的名称、方法名,方法参数、执行的表达式等等。

1、配置定时任务需要的线程池

import org.springframework.context.annotation.bean;
import org.springframework.context.annotation.configuration;
import org.springframework.scheduling.taskscheduler;
import org.springframework.scheduling.concurrent.threadpooltaskscheduler;

// 配置定时任务线程池
@configuration
public class schedulingconfig {
    @bean
    public taskscheduler taskscheduler() {
        threadpooltaskscheduler taskscheduler = new threadpooltaskscheduler();
        // 定时任务执行线程池核心线程数
        taskscheduler.setpoolsize(4);
        taskscheduler.setremoveoncancelpolicy(true);
        taskscheduler.setthreadnameprefix("taskschedulerthreadpool-test-");
        return taskscheduler;
    }
}

2、创建scheduledfuture的包装类

// scheduledfuture的包装类
public final class scheduledtask {

    volatile scheduledfuture<?> future;

    /**
     * 取消定时任务
     */
    public void cancel() {
        scheduledfuture<?> future = this.future;
        if (future != null) {
            future.cancel(true);
        }
    }
}

3、注册定时任务,增加、删除任务

/**
 * 添加定时任务注册,用来增加、删除定时任务。
 */
@component
public class crontaskregistrar implements disposablebean {

    private final map<runnable, scheduledtask> scheduledtasks = new concurrenthashmap<>(16);

    @autowired
    private taskscheduler taskscheduler;

    public taskscheduler getscheduler() {
        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, schedulecrontask(crontask));
        }
    }
    // 移除定时任务
    public void removecrontask(runnable task) {
        scheduledtask scheduledtask = this.scheduledtasks.remove(task);
        if (scheduledtask != null)
            scheduledtask.cancel();
    }

    public scheduledtask schedulecrontask(crontask crontask) {
        scheduledtask scheduledtask = new scheduledtask();
        scheduledtask.future = this.taskscheduler.schedule(crontask.getrunnable(), crontask.gettrigger());

        return scheduledtask;
    }


    @override
    public void destroy() {
        for (scheduledtask task : this.scheduledtasks.values()) {
            task.cancel();
        }
        this.scheduledtasks.clear();
    }
}

4、创建具体执行bean中方法的类

// 添加runnable接口实现类,被定时任务线程池调用,用来执行指定bean里面的方法。
@slf4j
public class schedulingrunnable implements runnable {

    // bean名称
    private string beanname;

    // 方法名称
    private string methodname;

    // 方法参数
    private string params;

    public schedulingrunnable(string beanname, string methodname) {
        this(beanname, methodname, null);
    }

    public schedulingrunnable(string beanname, string methodname, string params) {
        this.beanname = beanname;
        this.methodname = methodname;
        this.params = params;
    }

    @override
    public void run() {
        log.info("定时任务开始执行 - bean:{},方法:{},参数:{}", beanname, methodname, params);
        long starttime = system.currenttimemillis();

        try {
            object target = springcontextutils.getbean(beanname);

            method method = null;
            if (stringutils.isnotempty(params)) {
                method = target.getclass().getdeclaredmethod(methodname, string.class);
            } else {
                method = target.getclass().getdeclaredmethod(methodname);
            }

            reflectionutils.makeaccessible(method);
            if (stringutils.isnotempty(params)) {
                method.invoke(target, params);
            } else {
                method.invoke(target);
            }
        } catch (exception ex) {
            log.error(string.format("定时任务执行异常 - bean:%s,方法:%s,参数:%s ", beanname, methodname, params), ex);
        }

        long times = system.currenttimemillis() - starttime;
        log.info("定时任务执行结束 - bean:{},方法:{},参数:{},耗时:{} 毫秒", beanname, methodname, params, times);
    }

    @override
    public boolean equals(object o) {
        if (this == o) return true;
        if (o == null || getclass() != o.getclass()) return false;
        schedulingrunnable that = (schedulingrunnable) o;
        if (params == null) {
            return beanname.equals(that.beanname) &&
                    methodname.equals(that.methodname) &&
                    that.params == null;
        }

        return beanname.equals(that.beanname) &&
                methodname.equals(that.methodname) &&
                params.equals(that.params);
    }

    @override
    public int hashcode() {
        if (params == null) {
            return objects.hash(beanname, methodname);
        }

        return objects.hash(beanname, methodname, params);
    }
}

5、从spring容器里获取bean

// 从spring容器里获取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);
    }

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

6、创建具体执行的bean以及方法

// 测试类
@component("testtask")
public class testtask {

    /**
     * 有参方法
     * @param params
     */
    public void taskwithparams(string params) {
        system.out.println("执行有参示例任务:" + params);
    }

    /**
     * 无惨方法
     */
    public void tasknoparams() {
        system.out.println("执行无参示例任务");
    }
}

7、接口测试类

@restcontroller
@requestmapping("/test")
public class testcontroller {

    @autowired
    private crontaskregistrar crontaskregistrar;

    @requestmapping("/addtest")
    public string addtest(boolean flg){
        schedulingrunnable task;
        if (flg) {
            // 创建无惨任务
            task = new schedulingrunnable("testtask", "tasknoparams");
        }
        else {
            task = new schedulingrunnable("testtask", "taskwithparams", "hello word");
        }
        crontaskregistrar.addcrontask(task, "0/1 * * * * *");
        return "ok";
    }

    @requestmapping("/updatetest")
    public string updatetest(boolean flg){
        //先移除再添加
        if(flg) {
            // 创建无惨任务
            schedulingrunnable task = new schedulingrunnable("testtask", "tasknoparams");
            crontaskregistrar.removecrontask(task);
        }
        else {
            schedulingrunnable task = new schedulingrunnable("testtask", "taskwithparams", "hello word");
            crontaskregistrar.removecrontask(task);
        }

        schedulingrunnable task = new schedulingrunnable("testtask", "taskwithparams", "hello word");
        crontaskregistrar.addcrontask(task, "0/5 * * * * *");
        return "ok";
    }

    @requestmapping("/deltest")
    public string deltest(){
        schedulingrunnable task = new schedulingrunnable("testtask", "tasknoparams");
        crontaskregistrar.removecrontask(task);
        return "ok";
    }

    @requestmapping("/starttest")
    public string starttest(boolean flg){
        /**
         * 启停定时任务的逻辑就是创建新的任务或者删除任务,参数一致即可
         * 可以结合数据库,将配置信息存入数据库
         */
        if(flg) {
            schedulingrunnable task = new schedulingrunnable("testtask", "tasknoparams");
            crontaskregistrar.addcrontask(task, "0/5 * * * * *");
        }
        else {
            schedulingrunnable task = new schedulingrunnable("testtask", "tasknoparams");
            crontaskregistrar.removecrontask(task);
        }
        return "ok";
    }
}

8、结合数据库,创建对应的实体

// 定时任务实体
@data
public class jobentity {
    /**
     * 任务id
     */
    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;

}

9、读取数据库要执行的任务

在程序启动的时候,读取数据库,并创建要执行任务

/**
 * @description:    初始化数据库任务,可以再程启动的时候加载数据库里的任务
 * @author: hk
 * @since: 2025/4/21 18:21
 */
@component
@slf4j
public class inittask implements commandlinerunner {

    @autowired
    private crontaskregistrar crontaskregistrar;

    @override
    public void run(string... args) {
        // 初始加载数据库里状态为正常的定时任务
/*        list<sysjobpo> joblist = service.getjoblist("1");
        if (collectionutils.isnotempty(joblist)) {
            for (sysjobpo job : joblist) {
                schedulingrunnable task = new schedulingrunnable(job.getbeanname(), job.getmethodname(), job.getmethodparams());
                crontaskregistrar.addcrontask(task, job.getcronexpression());
            }

            log.info("定时任务已加载完毕...");
        }*/
    }
}

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com