首页 > 其他分享 >Spring Schedule定时任务进阶篇(调度器)

Spring Schedule定时任务进阶篇(调度器)

时间:2024-03-16 11:24:23浏览次数:26  
标签:methodName String Schedule Spring beanName 进阶篇 org import public

Spring Schedule背后支持多种任务调度方案,如JDK Timer、concurrent包下的ScheduledExecutorService以及Quartz等。Spring通过封装这些底层实现,为开发者提供了统一的接口和配置方式来处理定时任务。

接下来通过SpringBoot+数据库来实现根据数据库数据来动态管理我们的定时任务,我这里使用的是ORACLE数据库

  1. 配置Spring Boot项目与Oracle或其他数据库连接,以便从数据库中读取定时任务信息。

       <!--oracle 依赖-->
            <dependency>
                <groupId>com.oracle.ojdbc</groupId>
                <artifactId>ojdbc8</artifactId>
                <scope>runtime</scope>
            </dependency>

     

  2. 在数据库中创建一个表来存储定时任务,包括但不限于以下列
    id(主键)
    
    bean_name(类名)
    
    method_name(方法名)
    
    cron_expression(定时表达式)
    
    status(状态)
    

      

  3. 创建一个TaskDefinition实体类来映射数据库表结构,例如:
    public class TQuzetJob{
        private Long id;
        private String beanName;
        private String methodName;
        private String cronExpression;
        private String jobStatus;
        // 构造函数、getter/setter等
    }
    

      

  4. 创建一个TaskSchedulerService,用于从数据库中加载任务并注册到调度器中:
    import org.springframework.scheduling.TaskScheduler;
    import org.springframework.scheduling.config.CronTask;
    
    @Service
    @Transactional
    public class DemoServiceImpl implements IDemoService {
    
        @Autowired
        private TQuzetJobMapper quzetJobMapper;
    
        @Autowired
        private ApplicationContext applicationContext;
    
        @Autowired
        private TaskScheduler taskScheduler;
    
        private final Map<String, ScheduledFuture<?>> scheduleMap = new ConcurrentHashMap<>(16);
    
        @Override
        public Result startTask() {
            //从数据库中获取需要添加到任务调度器的信息
            QueryWrapper<TQuzetJob> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("JOB_STATUS","0");
            List<TQuzetJob> jobList = quzetJobMapper.selectList(queryWrapper);
    
            jobList.forEach(job -> {
                try {
                    SchedulingRunnable task = new SchedulingRunnable(job.getBeanName(), job.getMethodName(), job.getMethodParams());
                    CronTask cronTask = new CronTask(task, job.getCronExpression());
                    //调度任务返回的ScheduledFuture对象,它是唯一可以取消调度任务的引用
                    ScheduledFuture<?> schedule = taskScheduler.schedule(cronTask.getRunnable(), cronTask.getTrigger());
                    scheduleMap.put(job.getId(),schedule);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            });
            return Result.ok("执行完毕");
        }
     }

     

  5. 创建一个定时任务执行类
    package com.config.quzet;
    
    import com.utils.SpringContextUtils;
    import org.apache.commons.lang3.StringUtils;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.util.ReflectionUtils;
    
    import java.lang.reflect.Method;
    import java.util.Objects;
    
    public class SchedulingRunnable implements Runnable {
        private static final Logger logger = LoggerFactory.getLogger(SchedulingRunnable.class);
    
        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() {
            logger.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) {
                ex.printStackTrace();
                logger.error(String.format("定时任务执行异常 - bean:%s,方法:%s,参数:%s ", beanName, methodName, params), ex);
            }
    
            long times = System.currentTimeMillis() - startTime;
            logger.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);
        }
    }

     

  6. 通过上下文工具类获取数据库配置的bean
    package com.utils;
    
    import org.springframework.beans.BeansException;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.ApplicationContextAware;
    import org.springframework.stereotype.Component;
    
    /**
     * spring上下文工具类,用于普通类调用springIOC中的对象
     * */
    @Component
    public class SpringContextUtils implements ApplicationContextAware {
    
        private static ApplicationContext applicationContext = null;
    
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            if (SpringContextUtils.applicationContext == null) {
                SpringContextUtils.applicationContext = applicationContext;
            }
        }
    
        /**
         * @apiNote 获取applicationContext
         */
        public static ApplicationContext getApplicationContext() {
            return applicationContext;
        }
    
        /**
         * @apiNote 通过name获取 Bean.
    
         */
        public static Object getBean(String name) {
            return getApplicationContext().getBean(name);
        }
    
        /**
         * @apiNote 通过class获取Bean.
         */
        public static <T> T getBean(Class<T> clazz) {
            return getApplicationContext().getBean(clazz);
        }
    
        /**
         * @apiNote 通过name, 以及Clazz返回指定的Bean
    
         */
        public static <T> T getBean(String name, Class<T> clazz) {
            return getApplicationContext().getBean(name, clazz);
        }
    
    }

     

  7. 上面这些都配置好后我们写一个测试用例
    package com.demo.controller;
    
    import com.commom.CommonConstant;
    import com.commom.HandleLog;
    import com.commom.Result;
    import com.demo.service.IDemoService;
    import com.modules.entity.vo.LoginVo;
    import io.swagger.annotations.Api;
    import io.swagger.annotations.ApiOperation;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RestController;
    
    @Component("Demo")
    @RestController
    @Api(tags="测试")
    public class DemoController {
    
        @Autowired
        private IDemoService service;
    
        @GetMapping(value ="/startTask")
        @ApiOperation(value ="测试从数据库读取启动定时任务")
        public Result startTask(){
            return service.startTask();
        }
    
        @ApiOperation(value ="定时任务")
        public void taskTest(){
            System.out.println("执行定时任务");
        }
    }

    调用接口就发现数据库内的定时任务已经加载到了taskScheduler任务调度器中了,

    在上面用到了一个非常关键的类TaskScheduler 它是来自于 org.springframework.scheduling.concurrent 包的一个接口。这个接口定义了调度任务的功能,允许应用程序异步地安排任务在未来某个时间点执行。

    在Spring Boot应用中,若要使用 TaskScheduler,通常需要导入如下的Maven依赖(如果使用的是Spring Boot,默认已经包含了此依赖,因为Spring Boot包含了完整的Spring框架):

  8. 从调度器中移除已经添加的定时任务:移除定时任务我们需要用到添加到调度器中得到的返回值ScheduledFutuer<?>
      @Override
        public Result stopTask(String id, Boolean b) {
            //从
            ScheduledFuture<?> scheduledFuture = scheduleMap.get(id);
            // 检查 ScheduledFuture 是否存在并取消任务
            if (scheduledFuture != null) {
                scheduledFuture.cancel(b); // 参数 false 表示不强制中断正在执行的任务
                // 如果需要强制中断,则传入 true
                // scheduledFuture.cancel(true);
    
                // 从 scheduleMap 中移除该任务
                scheduleMap.remove(id);
            }
            return Result.ok("执行完毕");
        }

    调用此方法后就可以发现定时任务已经停止

     

标签:methodName,String,Schedule,Spring,beanName,进阶篇,org,import,public
From: https://www.cnblogs.com/HQ0422/p/18076844

相关文章

  • springboot/java/php/node/python农产品销售系统小程序【计算机毕设】
    本系统(程序+源码)带文档lw万字以上  文末可领取本课题的JAVA源码参考系统程序文件列表系统的选题背景和意义选题背景:随着互联网技术的迅猛发展,传统农产品销售模式正逐步向线上转移。小程序作为移动互联网的一种轻量级应用,因其开发成本低、传播快、使用方便等特点,成为连接......
  • Spring Bean的生命周期流程
    前言Java中的公共类称之为JavaBean,而Spring中的Bean指的是将对象的生命周期,交给SpringIoC容器来管理的对象。所以Spring中的Bean对象在使用时,无需通过new来创建对象,只需要通过DI(依赖注入),从Spring中取出要使用的对象即可。在Spring框架中单实例Bean的初始化......
  • Spring框架与其他框架(如Struts、Hibernate等)相比有何独特之处?Spring框架的主要优点有
    Spring框架与其他框架(如Struts、Hibernate等)相比有何独特之处?在Spring框架的面试中,可能会被问到的问题涵盖多个方面,包括但不限于以下几个方面:Spring框架的基本理解:请简述一下你对Spring框架的理解。Spring框架的主要优点有哪些?Spring框架与其他框架(如Struts、Hibernate......
  • 解释一下Spring中的IoC(控制反转)和DI(依赖注入)是什么,它们之间有何关系?Spring的Bean的生
    解释一下Spring中的IoC(控制反转)和DI(依赖注入)是什么,它们之间有何关系?在Spring框架中,IoC(控制反转)和DI(依赖注入)是两个核心概念,它们对于实现松耦合和高度可配置的应用程序至关重要。IoC(控制反转):IoC,即控制反转,是一种设计思想,其核心思想是将原本由代码直接操控的对象的调用权交......
  • Spring 5.x 源码之旅-59AOP事务的初始化流程一
    作者简介:大家好,我是smart哥,前中兴通讯、美团架构师,现某互联网公司CTO联系qq:184480602,加我进群,大家一起学习,一起进步,一起对抗互联网寒冬学习必须往深处挖,挖的越深,基础越扎实!阶段1、深入多线程阶段2、深入多线程设计模式阶段3、深入juc源码解析阶段4、深入jdk其余源码解析......
  • Spring 5.x 源码之旅-59AOP事务的初始化流程二
    作者简介:大家好,我是smart哥,前中兴通讯、美团架构师,现某互联网公司CTO联系qq:184480602,加我进群,大家一起学习,一起进步,一起对抗互联网寒冬学习必须往深处挖,挖的越深,基础越扎实!阶段1、深入多线程阶段2、深入多线程设计模式阶段3、深入juc源码解析阶段4、深入jdk其余源码解析......
  • Springboot
    SpringBootSpringBoot集成PageHelper加入依赖<dependency><groupId>com.github.pagehelper</groupId><artifactId>pagehelper-spring-boot-starter</artifactId><version>1.4.6</version></dependency>写代码/......
  • springboot仓库管理系统(附:源码+课件)
    项目介绍:管理员system123456客户表(ID客户名称邮编客户地址客户电话联系人联系人电话开户行账号邮箱)供应商表(ID供应商名称邮编供应商地址供应商电话联系人联系人电话开户行账号邮箱)商品表(ID商品名称供应商产地商品规格商品包装生产批号批准文号商......
  • 智慧医养大数据公共服务系统(JSP+java+springmvc+mysql+MyBatis)
    本项目包含程序+源码+数据库+LW+调试部署环境,文末可获取一份本项目的java源码和数据库参考。项目文件图 项目介绍随着老龄化社会的到来和大数据技术的发展,智慧医养结合的公共服务系统成为社会关注的热点。这一系统能够集成医疗、养老、健康管理等数据,通过智能分析和处理,......
  • SpringBoot鲜花商城管理系统Java
    一 设计背景1.1课题现状1.2课题意义二 开发环境及技术架构22.1开发环境与工具2.2技术架构三 需求分析3.1系统目标3.2功能需求3.3性能要求四 系统设计4.1系统功能总体设计4.2系统详细设计4.2.1用户登录4.2.2用户注册4.2.3管理员登......