前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >SpringTask实现数据库中获取任务、调用方法、方法参数

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

作者头像
devi
发布2021-08-19 15:01:38
8470
发布2021-08-19 15:01:38
举报
文章被收录于专栏:搬砖记录搬砖记录

目录

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中的方法,传入全类名然后反射即可

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

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2020-08-22 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 目录
  • 1. 引言
  • 2. 数据表设计
  • 3. 代码
  • 4. 用途
  • 5. 待完善:
相关产品与服务
数据库
云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档