一篇简单的文章带你玩转SpringBoot 之定时任务详解

本文涉及的产品
RDS Agent Manager,2核4GB
RDS MySQL DuckDB 分析主实例,集群系列 4核8GB
RDS AI 助手,专业版
简介: SpringBoot 之定时任务详解

序言

使用SpringBoot创建定时任务非常简单,目前主要有以下三种创建方式:

  • 一、基于注解(@Scheduled)
  • 二、基于接口(SchedulingConfigurer) 前者相信大家都很熟悉,但是实际使用中我们往往想从数据库中读取指定时间来动态执行定时任务,这时候基于接口的定时任务就派上用场了。
  • 三、基于注解设定多线程定时任务

静态:基于注解

基于注解@Scheduled默认为单线程,开启多个任务时,任务的执行时机会受上一个任务执行时间的影响。

创建定时器

使用SpringBoot基于注解来创建定时任务非常简单,只需几行代码便可完成。

代码如下:

@Component
@Configuration      //1.主要用于标记配置类,兼备Component的效果。
@EnableScheduling   // 2.开启定时任务
public class SaticScheduleTask {
    //3.添加定时任务
    @Scheduled(cron = "0/5 * * * * ?")
    //或直接指定时间间隔,例如:5秒
    //@Scheduled(fixedRate=5000)
    private void configureTasks() {
        System.err.println("执行静态定时任务时间: " + LocalDateTime.now());
    }
}

Cron表达式参数分别表示:

  • */5 * * * * ? :每隔5秒执行一次
  • 0 */1 * * * ? :每隔1分钟执行一次
  • 0 0 23 * * ? :每天23点执行一次
  • 0 0 1 * * ? :每天凌晨1点执行一次:
  • 0 0 1 1 * ? :每月1号凌晨1点执行一次
  • 0 0 23 L * ? : 每月最后一天23点执行一次
  • 0 0 1 ? * L :每周星期天凌晨1点实行一次
  • 0 26,29,33 * * * ? : 在26分、29分、33分执行一次
  • 0 0 0,13,18,21 * * ? : 每天的0点、13点、18点、21点都执行一次

Cron表达式由7个部分组成,各部分用空格隔开,Cron表达式的7个部分从左到右代表的含义如下:

秒 分 时 日 月 周 年

其中 年是可选的。

              字段名                                   允许的值             允许的特殊字符
0-59 ,- * /
0-59 ,- * /
0-23 ,- * /
1-31 ,- * ? / L W C
1-12 or JAN-DEC ,- * /
1-7 or SUN-SAT ,- * ? / L C #
年(可选字段) empty,1970-2099 ,- * /

符号说明

  • ,:表示列出枚举值值。例如在分使用5,20,则意味着在5和20分每分钟触发一次。
  • -:表示范围。例如在分使用5-20,表示从5分到20分钟每分钟触发一次。
  • * :表示匹配该域的任意值。假如在分域使用*,即表示每分钟都会触发事件。
  • / :表示起始时间开始触发,然后每隔固定时间触发一次,例如在Minutes域使用5/20,则意味着5分钟触发一次,而25,45等分别触发一次。
  • ? :只能用在周和日。它也匹配域的任意值,但实际不会。因为周和日会相互影响。例如想在每月的20日触发调度,不管20日到底是星期几,则只能使用如下写法: 13 13 15 20 * ?,其中最后一位只能用?,而不能使用,如果使用表示不管星期几都会触发,实际上并不是这样。
  • L : 表示最后,只能出现在日和周,如果在日使用5L,意味着在最后的一个星期四触发。
  • W:表示有效工作日(周一到周五),只能出现在周域,系统将在离指定日期的最近的有效工作日触发事件。例如:在日使用5W,如果5日是星期六,则将在最近的工作日:星期五,即4日触发。如果5日是星期天,则在6日触发;如果5日在星期一到星期五中的一天,则就在5日触发。另外一点,W的最近寻找不会跨过月份。
  • #:用于确定每个月第几个星期几,只能出现在周。例如在4#2,表示某月的第二个星期三。

@Scheduled:除了支持灵活的参数表达式cron之外,还支持简单的延时操作,例如 fixedDelay ,fixedRate 填写相应的毫秒数即可。

动态:基于接口

基于接口(SchedulingConfigurer)

导入依赖包pom.xml

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
        <version>2.0.4.RELEASE</version>
    </parent>
    <dependencies>
        <dependency><!--添加Web依赖 -->
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency><!--添加MySql依赖 -->
             <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency><!--添加Mybatis依赖 配置mybatis的一些初始化的东西-->
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.3.1</version>
        </dependency>
        <dependency><!-- 添加mybatis依赖 -->
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.5</version>
            <scope>compile</scope>
        </dependency>
    </dependencies>

添加数据库记录:

DROP DATABASE IF EXISTS `socks`;
CREATE DATABASE `socks`;
USE `SOCKS`;
DROP TABLE IF EXISTS `cron`;
CREATE TABLE `cron`  (
  `cron_id` varchar(30) NOT NULL PRIMARY KEY,
  `cron` varchar(30) NOT NULL  
);
INSERT INTO `cron` VALUES ('1', '0/5 * * * * ?');

然后在项目中的application.properties 文件中

添加数据源:

server.port = 8888
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url= jdbc:mysql://127.0.0.1:3306/ordersystem?useUnicode=true&characterEncoding=utf8&useSSL=true&serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=root

创建定时器

数据库准备好数据之后,我们编写定时任务,注意这里添加的是TriggerTask,目的是循环读取我们在数据库设置好的执行周期,以及执行相关定时任务的内容。

@Component
@Configuration      //1.主要用于标记配置类,兼备Component的效果。
@EnableScheduling   // 2.开启定时任务
public class DynamicScheduleTask implements SchedulingConfigurer {
    @Mapper
    public interface CronMapper {
        @Select("select cron from cron limit 1")
        public String getCron();
    }
    @Autowired      //注入mapper
    @SuppressWarnings("all")
    CronMapper cronMapper;
    /**
     * 执行定时任务.
     */
    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        taskRegistrar.addTriggerTask(
                //1.添加任务内容(Runnable)
                () -> System.out.println("执行动态定时任务: " + LocalDateTime.now().toLocalTime()),
                //2.设置执行周期(Trigger)
                triggerContext -> {
                    //2.1 从数据库获取执行周期
                    String cron = cronMapper.getCron();
                    //2.2 合法性校验.
                    if (StringUtils.isEmpty(cron)) {
                        // Omitted Code ..
                    }
                    //2.3 返回执行周期(Date)
                    return new CronTrigger(cron).nextExecutionTime(triggerContext);
                }
        );
    }
}

注意:如果在数据库修改时格式出现错误,则定时任务会停止,即使重新修改正确;此时只能重新启动项目才能恢复。

多线程

基于注解设定多线程定时任务

创建多线程定时任务

例子:我下订单后,要求订单在30分钟内如果未支付则自动取消该订单

package com.shijiu.component;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.shijiu.dao.mapper.ShopOrderMapper;
import com.shijiu.pojo.ShopOrder;
import com.shijiu.service.ShopOrderService;
import com.shijiu.service.impl.ShopOrderServiceImpl;
import io.swagger.annotations.ApiOperation;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.List;
import java.util.stream.Collectors;
/**
 * @Author 
 * @Date Created in  2023/4/27 9:52
 * @DESCRIPTION:
 * @Version V1.0
 */
//@Component注解用于对那些比较中立的类进行注释;
//相对与在持久层、业务层和控制层分别采用 @Repository、@Service 和 @Controller 对分层中的类进行注释
@Component
// 1.开启定时任务
@EnableScheduling
/**
  2.开启多线程(定时任务检查删除订单)
 */
@EnableAsync
@SuppressWarnings("all")
public class MultithreadScheduleTask {
   @Resource
   private ShopOrderMapper shopOrderMapper;
   @Async
   @Scheduled(fixedDelay = 60000)
   @ApiOperation("/* 间隔1分钟定时检查数据库 */")
   public void first() throws InterruptedException {
      LambdaQueryWrapper<ShopOrder> wrapper = new LambdaQueryWrapper<>();
      wrapper.eq(ShopOrder::getStatus,"0").eq(ShopOrder::getIsDeleted,"0");
      //拿到所有的订单
      List<ShopOrder> shopOrderList = shopOrderMapper.selectList(wrapper);
      List<ShopOrder> filteredOrders = shopOrderList.stream()
              .filter(order -> Math.ceil(DateUtil.betweenMs(order.getCreateTime(), order.getOverTime())) / 60000.0  > 30) // 保留订单结束时间与创建时间间隔超过30分钟的订单
              .collect(Collectors.toList()); // 将满足条件的订单收集到一个新的集合中
      //double result = Math.ceil(DateUtil.betweenMs("2023-04-27 10:05:03", "2023-04-27 10:07:02") / 60000.0);
       // 对筛选出来的订单执行xxx操作
      System.out.println(filteredOrders);
      filteredOrders.forEach(order -> {
         // 对订单执行xxx操作,例如:
         String orderId = order.getId();
         ShopOrder shopOrder = new ShopOrder();
         shopOrder.setId(shopOrderId);
         shopOrder.setIsDeleted("1");
         shopOrderMapper.updateById(shopOrder);
      });
   }
}

打印结果:

961bde67189f443298ac35a53724f573.png




相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。 &nbsp; 相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情:&nbsp;https://www.aliyun.com/product/rds/mysql&nbsp;
目录
相关文章
|
安全 Java 持续交付
如何实现上传jar直接部署成功,这篇文章直接带你上手springboot实现jar包热更新!
本文详细讲解了在Spring Boot应用中实现Jar包热更新的实践方法。通过自定义类加载器(`HotClassLoader`),动态加载和卸载指定目录下的Jar包,结合Spring Bean动态注册机制,使新加载的类能够被Spring容器管理。同时,提供了文件上传接口,方便用户手动触发Jar包更新。文章还强调了安全性、依赖管理和线程安全等注意事项,并给出了测试步骤和总结,帮助开发者高效实现热更新功能,减少服务中断和提升开发效率。
|
JavaScript Java 测试技术
基于springboot+vue.js+uniapp的小区物流配送系统附带文章源码部署视频讲解等
基于springboot+vue.js+uniapp的小区物流配送系统附带文章源码部署视频讲解等
605 5
|
JavaScript Java 测试技术
基于springboot+vue.js+uniapp宿舍管理系统的附带文章源码部署视频讲解等
基于springboot+vue.js+uniapp宿舍管理系统的附带文章源码部署视频讲解等
358 3
|
JavaScript Java 测试技术
基于springboot+vue.js+uniapp的家政平台附带文章源码部署视频讲解等
基于springboot+vue.js+uniapp的家政平台附带文章源码部署视频讲解等
463 4
|
JavaScript Java 测试技术
基于springboot+vue.js+uniapp的客户关系管理系统附带文章源码部署视频讲解等
基于springboot+vue.js+uniapp的客户关系管理系统附带文章源码部署视频讲解等
473 2
|
JavaScript Java 测试技术
基于springboot+vue.js+uniapp的宠物援助平台附带文章源码部署视频讲解等
基于springboot+vue.js+uniapp的宠物援助平台附带文章源码部署视频讲解等
797 4
|
JavaScript Java 测试技术
基于springboot+vue.js+uniapp的宠物医院系统附带文章源码部署视频讲解等
基于springboot+vue.js+uniapp的宠物医院系统附带文章源码部署视频讲解等
374 2
|
JavaScript Java 测试技术
基于springboot+vue.js+uniapp的宠物交易平台附带文章源码部署视频讲解等
基于springboot+vue.js+uniapp的宠物交易平台附带文章源码部署视频讲解等
622 4
|
JavaScript Java 测试技术
基于springboot+vue.js+uniapp的实验室安全考试系统附带文章源码部署视频讲解等
基于springboot+vue.js+uniapp的实验室安全考试系统附带文章源码部署视频讲解等
404 2
|
JavaScript Java 测试技术
基于springboot+vue.js+uniapp的学生网课学习效果评价附带文章源码部署视频讲解等
基于springboot+vue.js+uniapp的学生网课学习效果评价附带文章源码部署视频讲解等
299 2