专栏首页搬砖记录SpringTask实现数据库中获取任务、调用方法、方法参数

SpringTask实现数据库中获取任务、调用方法、方法参数

目录

1. 引言

quartz对数据库支持非常强大,但是用起来并没有SpringTask那么简单。 因此,个人造了一点小轮子,让SpringTask拥有类似quartz的功能(当然没有那么完善)。

转载请注明出处,欢迎留言交流。

2. 数据表设计

设计思路: exec_time字段提供对固定时间执行一次的支持,也可以通过cron字段,实现任意触发时间。

method_name字段表示需要触发的方法名; args则是method_name对应方法的参数值; args_type则是args的具体类型(暂时仅支持基本数据类型以及包装类)。

3. 代码

注入TaskScheduler

@Configuration
@EnableScheduling
public class SchedulerConfig {
    @Bean
    public TaskScheduler taskScheduler() {
        ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
        //线程池大小
        scheduler.setPoolSize(10);
        //线程名字前缀
        scheduler.setThreadNamePrefix("spring-task-thread");
        return scheduler;
    }
}

执行引擎

@Component
public class JobEngine {

    @Autowired
    TaskScheduler scheduler;

    public void execute(Runnable runnable, Date date){
        // SpringTask不支持年  Seconds Minutes Hours DayofMonth Month DayofWeek
        String cron = DateUtil.format(date, "ss mm HH dd MM ?");
        scheduler.schedule(runnable,new CronTrigger(cron));
    }

    public void execute(Runnable runnable, String cron){
        scheduler.schedule(runnable,new CronTrigger(cron));
    }
}

执行器

/**
 * 定时扫描数据库执行任务
 */
@Slf4j
@Component
public class JobService {

    @Autowired
    AppJobMapper jobMapper;
    
    @Autowired
    JobEngine jobEngine;

    @Scheduled(cron = "*/5 * * * * ?")
    public void execute() {
        // 查询出所有未执行的任务 isExec=0 && now<execTime
        List<AppJob> jobs = jobMapper.findTask();
        if (jobs.size() != 0) {
            jobs.forEach(job -> {
                // 获取数据库数据
                String[] strArgs = job.getArgs().split(",");
                Date date = job.getExecTime();
                String methodName = job.getMethodName();
                String[] classTypes = job.getArgsType().split(",");

				// 反射装配参数以及对应类型
                List<Class<?>> argsTypeList = new ArrayList<>();
                List<Object> argsList = new ArrayList<>();
                for(int i=0;i<strArgs.length;i++){
                    String classType = classTypes[i];

                    if ("string".equalsIgnoreCase(classType)) {
                        classType = "java.lang.String";
                        argsList.add(String.valueOf(strArgs[i]));
                    }else if ("integer".equalsIgnoreCase(classType) || "int".equalsIgnoreCase(classType)) {
                        classType = "java.lang.Integer";
                        argsList.add(Integer.valueOf(strArgs[i]));
                    }else if ("long".equalsIgnoreCase(classType)) {
                        classType = "java.lang.Long";
                        argsList.add(Long.valueOf(strArgs[i]));
                    }else if ("double".equalsIgnoreCase(classType)) {
                        classType = "java.lang.Double";
                        argsList.add(Double.valueOf(strArgs[i]));
                    }else if ("boolean".equalsIgnoreCase(classType)) {
                        classType = "java.lang.Boolean";
                        argsList.add(Boolean.valueOf(strArgs[i]));
                    }
                    try {
                        Class<?> aClass = Class.forName(classType);
                        argsTypeList.add(aClass);
                    } catch (ClassNotFoundException e) {
                        log.error("只支持基本的数据类型以及包装类,非法类型:{}",classType);
                    }
                }
				// List转为Array,invoke要求
                Object[] args = new Object[argsList.size()];
                Class<?>[] classes = new Class[argsTypeList.size()];
                argsTypeList.toArray(classes);
                argsList.toArray(args);

                // 创建定时任务
                log.info("创建定时任务{}", job);
                Runnable runnable = () -> {
                    // 可能已经被执行,检查isExec是否为1
                    if (jobMapper.beforeExec(job.getJobId()) !=null) {
                        log.info("定时任务{}已被执行", job);
                        return;
                    }
                    try {
                        Method method = this.getClass().getMethod(methodName, classes);
                        if (strArgs.length > 0) {
                            method.invoke(this, args);
                        } else {
                            method.invoke(this);
                        }
                        // 更新状态 设isExec=1
                        if (jobMapper.afterExec(job.getJobId()) != 1) {
                            log.error("定时任务{}执行完毕后状态更新失败", job);
                        }
                    } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
                        log.error("定时任务{}执行完毕失败:{}", job,e.getMessage());
                    }
                };
                if (date == null) {
                    jobEngine.execute(runnable, job.getCron());
                } else {
                    jobEngine.execute(runnable, date);
                }
            });
        }
    }
    
    public void test(Integer a, Double b) {
        System.out.println("扫描数据库执行测试方法,参数:" + a + b);
    }
}

4. 用途

  1. 将时间跨度较高的任务加到数据表中(比如一个月执行一次),由JobService.execute方法,定时扫描数据库执行,能够避免服务停止导致的定时任务丢失。
  2. 可以将JobService.execute方法中的代码抽出,作为其他工具类使用(比如按扫描频度、执行功能等增加多种不同的execute)
  3. 本人目前的使用步骤:
    1. 在JobService类中添加可能的方法
    2. 在满足创建定时任务的地方,通过JobMapper创建定时任务保存到数据库
    3. 设置JobService.execute扫描时间

5. 待完善:

  1. 如果扫描频度范围内可能多次扫描数据库(比如上述方法我设置的是每5秒执行一次扫描,那么在数据表中的人物肯定会被多次扫描),那么任务会被重复创建。可以通过队列解决
  2. 若要支持非基本类型,可以考虑传入Class类型
  3. 反射执行的方法必须是JobService中的方法,传入全类名然后反射即可

现在比较忙,后续会把这个补充完成。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Thinkphp5框架实现获取数据库数据到视图的方法

    本文实例讲述了Thinkphp5框架实现获取数据库数据到视图的方法。分享给大家供大家参考,具体如下:

    砸漏
  • PHP实现动态获取函数参数的方法示例

    PHP 在用户自定义函数中支持可变数量的参数列表。其实很简单,只需使用 func_num_args() , func_get_arg() ,和 func_get...

    用户8660814
  • mall整合SpringTask实现定时任务

    Seconds Minutes Hours DayofMonth Month DayofWeek

    macrozheng
  • 那些年,我们追过的“定时调度”

    定时调度 作为MadPecker的后端开发人员,我们总会遇到这样的业务场景:每周同步一批数据;每半个小时检查一遍服务器运行状况;每天早上八点给用户发送一份包含今...

    MadPecker
  • 数据访问函数库的使用方法(二)—— 获取记录集和使用事务的方法

    使用SQL语句来获取记录集的方法 string sql = "select col1,col2,col3  from TableName where "; ...

    用户1174620
  • SpringTask任务案例源码实现

    写在开始 一般来说,无论是生活中或者项目中都会用到定时任务。比如我自己来说,每天晚上写一篇博客,当然这个任务的时间点可能不是那么准时。更多的例子,比如我们项目中...

    小柒2012
  • HttpURLConnection和okHttp两种获取网络数据的实现方法

    1.<uses-permission android:name=”android.permission.INTERNET”/

    砸漏
  • 最新!中国天气网api接口调用,key获取方式,数据请求秘钥获取,城市id获取方法

    以前的天气获取方式已经不支持了,虽然能获取到数据,但是获取到的信息已经不对了。 中国天气网提供的最新接口需要数据请求秘钥key。而且有效期只有7天,用完了还要...

    小蓝枣
  • Linux下Mysql定时任务备份数据的实现方法

    备份是容灾的基础,是指为防止系统出现操作失误或系统故障导致数据丢失,而将全部或部分数据集合从应用主机的硬盘或阵列复制到其它的存储介质的过程。而对于一些网站、系统...

    砸漏
  • Android拦截并获取WebView内部POST请求参数的实现方法

    有些时候自家APP中嵌入的H5页面并不是自家的。但是很多时候又想在H5不知情的情况下获取H5内部请求的参数,这应该怎么做到呢?

    砸漏
  • PHP获取数据库表中的数据插入新的表再原删除数据方法

    砸漏
  • SpringBoot 整合 Quartz 实现 JAVA 定时任务的动态配置

    首先说下这次主题,动态配置。没接触过定时任务的同学可以先看下此篇:JAVA定时任务实现的几种方式

    业余草
  • Spring Boot 整合 Quartz 实现 Java 定时任务的动态配置

    https://www.cnblogs.com/laoyeye/p/6530791.html

    良月柒
  • Android编程实现在自定义对话框中获取EditText中数据的方法

    本文实例讲述了Android编程实现在自定义对话框中获取EditText中数据的方法。分享给大家供大家参考,具体如下:

    砸漏
  • 使用Spring Task轻松完成定时任务

      最近项目中需要使用到定时任务进行库存占用释放的需求,就总结了如何使用Spring Task进行简单配置完成该需求,本文介绍Spring3.0以后自定义开发的...

    阿豪聊干货
  • SpringBoot 整合 Quartz 实现 JAVA 定时任务的动态配置

    来源:https://www.cnblogs.com/laoyeye/p/9352002.html老爷爷的博客园

    java进阶架构师
  • PHP实现获取毫秒时间戳的方法【使用microtime()函数】

    php本身没有提供返回毫秒数的函数,但提供了一个microtime()函数,借助此函数,可以很容易定义一个返回毫秒数的函数。

    砸漏
  • Django中使用Json返回数据的实现方法

    在Django中,使用JSON传输数据,有两种方式,一种是使用Python的JSON包,一种是使用Django的JsonResponse

    砸漏
  • springboot 整合 MongoDB 实现登录注册,html 页面获取后台参数的方法

    Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而...

    Krry

扫码关注云+社区

领取腾讯云代金券