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