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

news/2025/2/22 5:06:30/

序言

使用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 = 8888spring.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 {@Mapperpublic interface CronMapper {@Select("select cron from cron limit 1")public String getCron();}@Autowired      //注入mapper@SuppressWarnings("all")CronMapper cronMapper;/*** 执行定时任务.*/@Overridepublic 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 {@Resourceprivate 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);});}
}

打印结果:

 


http://www.ppmy.cn/news/55170.html

相关文章

Java——装箱和拆箱

一.装箱和拆箱的概念 基本数据(Primitive)类型的自动装箱(autoboxing)、拆箱(unboxing)是自J2SE 5.0开始提供的功能。Java语言规范中说道&#xff1a;在许多情况下包装与解包装是由编译器自行完成的&#xff08;在这种情况下包装称为装箱&#xff0c;解包装称为拆箱&#xff09…

AcWing55. 连续子数组的最大和

输入一个 非空 整型数组&#xff0c;数组里的数可能为正&#xff0c;也可能为负。 数组中一个或连续的多个整数组成一个子数组。 求所有子数组的和的最大值。 要求时间复杂度为 O(n)。 数据范围 数组长度 [1,1000]。 数组内元素取值范围 [−200,200] 样例 输入&#xff1a;[1,…

行为识别 Activity Recognition

行为识别 行为检测是一个广泛的研究领域&#xff0c;其应用包括安防监控、健康医疗、娱乐等。 课程大纲 导论 图卷积在行为识别中的应用&#xff1a;论文研读&#xff0c;代码解读&#xff0c;实验 Topdown关键点检测中的hrnet&#xff1a;论文研读&#xff0c;代码解读&a…

jsp家庭农场投入品信息管理系统Myeclipse开发mysql数据库web结构jsp编程计算机网页项目

一、源码特点 jsp家庭农场投入品信息管理系统是一套完善的java web信息管理系统 serlvet dao bean 开发&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为TOMCAT7.0,Myeclipse8.5开发…

常见的HTTP状态码列表

HTTP状态码列表 状态码 状态码英文名称 中文描述 1xx&#xff08;信息性状态码&#xff09;&#xff1a;请求已被接受&#xff0c;需要继续处理。 100 Continue 继续。客户端应继续其请求 101 Switching Protocols 切换协议。服务器根据客户端的请求切换协议…

机器视觉工程师,听我一句劝,别去外包,干了三年,废了....对女人没了兴趣

​外包三年,干了就废,最后只会安装软件。 对于年轻人来说,需要工作,更需要生活。 对于年轻人来说,需要努力,更需要“面包”。 对于年轻人来说,需要规划,更需要发展。 对于外包,虽说废的不是很彻底,但那三年几乎是出差了三年、玩了三年、荒废了三年,那三年,技术…

jsonp的工作原理

JavaScript是一种在Web开发中经常使用的前端动态脚本技术。在JavaScript中&#xff0c;有一个很重要的安全性限制&#xff0c;被称为“Same-Origin Policy”&#xff08;同源策略&#xff09;。 这一策略对于JavaScript代码能够访问的页面内容做了很重要的限制&#xff0c;即J…

类和对象(中)

类和对象------中 类的6个默认成员函数构造函数概念特性 析构函数概念特性 拷贝构造函数概念特征 赋值运算符重载运算符重载赋值运算符重载前置和后置重载 日期类的实现const成员函数取地址及const取地址操作符重载结语 类的6个默认成员函数 如果一个类中什么成员都没有&#x…