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

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: 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




相关实践学习
基于CentOS快速搭建LAMP环境
本教程介绍如何搭建LAMP环境,其中LAMP分别代表Linux、Apache、MySQL和PHP。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
22天前
|
存储 Java Spring
【八】springboot整合定时任务
【八】springboot整合定时任务
24 0
|
22天前
|
Java 调度
SpringBoot中实现定时任务(Quartz)(二)
SpringBoot中实现定时任务(Quartz)
30 0
SpringBoot中实现定时任务(Quartz)(二)
|
22天前
|
Java 调度 Spring
SpringBoot中实现定时任务(Quartz)(一)
SpringBoot中实现定时任务(Quartz)
45 0
SpringBoot中实现定时任务(Quartz)(一)
|
7月前
|
Java 关系型数据库 MySQL
SpringBoot实现定时任务
SpringBoot实现定时任务
|
11月前
|
Java 容器
SpringBoot 整合定时任务
SpringBoot 整合定时任务
|
12月前
|
Java
springboot 定时任务遇到的坑
springboot 定时任务遇到的坑
167 0
|
Java 容器 Spring
SpringBoot定时任务源码分析
写作目的 最近看了一篇博客 “Spring Boot实现定时任务的动态增删启停” ,其实实现这个需求的前提是你要搞明白 定时任务 的实现原理,这样你才有可能实现定时任务的动态增删启停,所以下面从源码的角度跟 SpringBoot定时任务原理。
210 0
SpringBoot定时任务源码分析
|
Java BI 调度
SpringBoot——整合定时任务
SpringBoot——整合定时任务
219 0
SpringBoot——整合定时任务
5、SpringBoot2.0实现定时任务(五)
引入相关定时任务相关的依赖
66 0
5、SpringBoot2.0实现定时任务(五)
|
Java
springboot配置定时任务
springboot配置定时任务
92 0

热门文章

最新文章