前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【springboot】2.3.7集成定时任务SchedulingConfigurer实例

【springboot】2.3.7集成定时任务SchedulingConfigurer实例

作者头像
用户5640963
发布2021-05-27 11:14:32
9610
发布2021-05-27 11:14:32
举报
文章被收录于专栏:卯金刀GG卯金刀GG

背景:

项目需要启动一个定时执行的任务,该任务的执行可以随时停止执行,要求在数据库配置执行规律。

调研:使用scheduling定时任务与springboot集成;

实例:

第一步:创建一个数据表,保存执行的任务

代码语言:javascript
复制
/*DDL 信息*/------------

CREATE TABLE `hk_sys_task` (
  `id` bigint(21) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `task_uuid` varchar(50) DEFAULT NULL COMMENT '任务UUID',
  `task_name` varchar(50) DEFAULT NULL COMMENT '任务名称',
  `task_cron` varchar(50) DEFAULT NULL COMMENT '任务定时表达式',
  `class_name` varchar(100) DEFAULT NULL COMMENT '任务类',
  `method_name` varchar(100) DEFAULT NULL COMMENT '任务方法',
  `task_type` int(1) DEFAULT NULL COMMENT '任务类型',
  `remark` varchar(250) DEFAULT NULL,
  `del_mark` int(1) DEFAULT '1',
  `flag` int(1) DEFAULT '1',
  `create_user` varchar(50) DEFAULT NULL,
  `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
  `modify_user` varchar(50) DEFAULT NULL,
  `modify_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8

2、创建springboot的SpringUtil用户获取bean

代码语言:javascript
复制
/**
 * @Author: Liu Yue
 * @Descripition:
 * @Date; Create in 2021/5/14 11:19
 **/
@Component
@Slf4j
public class SpringUtil implements ApplicationContextAware {
    private static ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        if(SpringUtil.applicationContext == null) {
            SpringUtil.applicationContext = applicationContext;
        }
        log.info("========ApplicationContext配置成功,在普通类可以通过调用SpringUtils.getAppContext()获取applicationContext对象,applicationContext={},",SpringUtil.applicationContext+"========");
    }

    //获取applicationContext
    public static ApplicationContext getApplicationContext() {
        return applicationContext;
    }

    //通过name获取 Bean.
    public static Object getBean(String name){
        return getApplicationContext().getBean(name);
    }

    //通过class获取Bean.
    public static <T> T getBean(Class<T> clazz){
        return getApplicationContext().getBean(clazz);
    }

    //通过name,以及Clazz返回指定的Bean
    public static <T> T getBean(String name,Class<T> clazz){
        return getApplicationContext().getBean(name, clazz);
    }
}

3、配置执行任务的类SysTaskConfig

代码语言:javascript
复制
/**
 * @Author: Liu Yue
 * @Descripition:
 * @Date; Create in 2021/5/14 9:34
 **/
@Lazy(value = false)
@Component
@Slf4j
public class SysTaskConfig implements SchedulingConfigurer {
    @Resource
    private HkSysTaskMapper hkSysTaskMapper;

    private static ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();

    private static Map<String,ScheduledFuture<?>> scheduledFutureMap = new HashMap<>();

    //从数据库里取得所有要执行的定时任务
    private List<HkSysTask> getAllTasks() {
        LambdaQueryWrapper<HkSysTask> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(HkSysTask::getDelMark,1)
                .eq(HkSysTask::getFlag,1);

        return hkSysTaskMapper.selectList(wrapper);
    }
    static {
        threadPoolTaskScheduler.initialize();
    }
    @Override
    public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
        List<HkSysTask> allTasks = getAllTasks();
        log.info("定时任务启动,预计启动任务数量={},; time={}",allTasks.size(),LocalDateTime.now());

        //校验数据(这个步骤主要是为了打印日志,可以省略)
        checkDataList(allTasks);

        //通过校验的数据执行定时任务
        int count = 0;
        if(allTasks.size()>0) {
            for (int i = 0; i < allTasks.size(); i++) {
                try {
                    scheduledTaskRegistrar.addTriggerTask(getRunnable(allTasks.get(i)), getTrigger(allTasks.get(i)));
                    count++;
                } catch (Exception e) {
                    log.error("定时任务启动错误:" + allTasks.get(i).getClassName() + ";" + allTasks.get(i).getMethodName() + ";" + e.getMessage());
                }
            }
        }
        log.info("定时任务实际启动数量="+count+"; time="+LocalDateTime.now());
    }
    private static Trigger getTrigger(HkSysTask task){
        return new Trigger() {
            @Override
            public Date nextExecutionTime(TriggerContext triggerContext) {
                //将Cron 0/1 * * * * ? 输入取得下一次执行的时间
                CronTrigger trigger = new CronTrigger(task.getTaskCron());
                Date nextExec = trigger.nextExecutionTime(triggerContext);
                return nextExec;
            }
        };

    }
    private static Runnable getRunnable(HkSysTask task){
        return new Runnable() {
            @Override
            public void run() {
                Class<?> clazz;
                try {
                    clazz = Class.forName(task.getClassName());
                    String className = lowerFirstCapse(clazz.getSimpleName());
                    Object bean = SpringUtil.getBean(className);
                    Method method = ReflectionUtils.findMethod(bean.getClass(), task.getMethodName());
                    ReflectionUtils.invokeMethod(method, bean);
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }
            }
        };
    }
    private List<HkSysTask> checkDataList(List<HkSysTask> list) {
        String errMsg="";
        for(int i=0;i<list.size();i++){
            if(!checkOneData(list.get(i)).equalsIgnoreCase("success")){
                errMsg+=list.get(i).getTaskName()+";";
                list.remove(list.get(i));
                i--;
            };
        }
        if(!StringUtils.isBlank(errMsg)){
            errMsg="未启动的任务:"+errMsg;
            log.error(errMsg);
        }
        return list;
    }
    private String checkOneData(HkSysTask task){
        String result="success";
        Class clazz= null;
        try {
            clazz = Class.forName(task.getClassName());
            //String className = lowerFirstCapse(clazz.getSimpleName());
            Object obj =SpringUtil.getBean(clazz);
            Method method = obj.getClass().getMethod(task.getMethodName(),null);
            String cron=task.getTaskCron();
            if(StringUtils.isBlank(cron)){
                result="定时任务启动错误,无cron:"+task.getTaskName();
                log.error(result);
            }
        } catch (ClassNotFoundException e) {
            result="定时任务启动错误,找不到类:"+task.getClassName()+ e.getMessage();
            log.error(result);
        } catch (NoSuchMethodException e) {
            result="定时任务启动错误,找不到方法,方法必须是public:"+task.getClassName()+";"+task.getMethodName()+";"+ e.getMessage();
            log.error(result);
        } catch (Exception e) {
            log.error(e.getMessage());
        }
        return result;
    }
    /**
     * 转换首字母小写
     *
     * @param str
     * @return
     */
    public static String lowerFirstCapse(String str) {
        char[] chars = str.toCharArray();
        chars[0] += 32;
        return String.valueOf(chars);
    }
    /**
     * 启动定时任务
     * @param task
     * @param
     */
    public static void start(HkSysTask task){
        ScheduledFuture<?> scheduledFuture = threadPoolTaskScheduler.schedule(getRunnable(task),getTrigger(task));
        scheduledFutureMap.put(task.getTaskUuid(),scheduledFuture);
        log.info("启动定时任务" + task.getId() );

    }
    /**
     * 取消定时任务
     * @param task
     */
    public static void cancel(HkSysTask task){

        ScheduledFuture<?> scheduledFuture = scheduledFutureMap.get(task.getId());

        if(scheduledFuture != null && !scheduledFuture.isCancelled()){
            scheduledFuture.cancel(Boolean.FALSE);
        }
        scheduledFutureMap.remove(task.getId());
        log.info("取消定时任务" + task.getId() );

    }
    /**
     * 编辑
     * @param task
     * @param
     */
    public static void reset(HkSysTask task){
        log.info("修改定时任务开始" + task.getId() );
        cancel(task);
        start(task);
        log.info("修改定时任务结束" + task.getId());
    }
}

4、定时执行的测试类

代码语言:javascript
复制
......
    public void test(){
        iHkUserService.userTaskRun();
    }
......

5、console输出的日志

每天进步一点点!

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

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

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档