在项目开发过程中,如果是一些简单的工程,非分布式工程,一般我们可以使用@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("定时任务已加载完毕..."); }*/ } }
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。
发表评论