一、Spring Task
1.1 介绍
Spring Task 是 Spring 框架内置的定时任务调度工具,可在约定的时间自动执行指定逻辑。
作用:定时执行 Java 代码
为什么要使用 Spring Task?
我们的项目目前还有两大问题还没有解决
- 支付超时的订单如何处理
- 派送中的订单一直没有点击完成如何处理
应用场景:
- 信用卡每月还款提醒

- 银行贷款还款提醒

- 火车票系统处理未支付订单

只要有定时处理需求,都可以使用 Spring Task
1.2 Cron 表达式
1.2.1 基本格式
Cron表达式是一个字符串,用于定义任务触发时间。
其由7个域组成,依次为:**秒、分、时、日、月、周、年(可选)**。
| 字段 | 允许值 |
|---|---|
| 秒 | 0-59 |
| 分 | 0-59 |
| 时 | 0-23 |
| 日 | 1-31 |
| 月 | 1-12 或 JAN-DEC |
| 周 | 0-7 (0或7=周日) 或 SUN-SAT |
| 年 | 可选,1970-2099 |
1.2.2 通配符含义
*:任意值(例如 * 在分钟位表示每分钟),:枚举值(例如1,5,10表示第1、5、10分钟)-:范围(例如1-5表示1到5)/:步长(例如0/5表示从0开始每5个单位)?:日和周互斥时使用,表示不指定L:最后(如日字段 L 表示当月最后一天,周字段 L 表示最后一个星期几)W:最近的工作日(如15W表示离本月15日最近的工作日)#:第几个星期几(如6#3表示当月第三个周五)
1.2.3 常见特殊用法:
每月某日最近的工作日
例:每月28号最近的工作日 → 如果28号是周六,则触发27号;如果是周日,则触发29号。
每月最后一天
例:2 月份最后一天可能是 28 或 29 号,不需手写,直接用生成器生成即可。

常用案例:
| 表达式 | 含义 |
|---|---|
0 0 12 * * ? |
每天中午12点执行 |
0 0/5 * * * ? |
每5分钟执行一次 |
0 0 9 ? * MON-FRI |
周一到周五上午9点执行 |
0 0 0 1 * ? |
每月1日0点执行 |
0 15 10 L * ? |
每月最后一天上午10:15执行 |
0 15 10 ? * 6L |
每月最后一个周五上午10:15执行 |
1.3 入门案例
1.3.1 启动类添加 @EnableScheduling 开启任务调度
1.3.2 定义定时任务类
自定义定时任务类
@Component
@Slf4j
public class MyTask {
@Scheduled(cron = "0/5 * * * * ?")
public void executeTask(){
log.info("定时任务执行:{}", new Date());
}
}
1.3.3 功能测试
启动服务 → 查看日志
二、订单状态定时处理功能
2.1 需求分析
用户下单后可能出现:
- 未支付:订单一直停留在“待支付”状态
- 未确认完成:收货后未点击“完成”,订单一直处于“派送中”
解决方案:
- 每分钟检查是否有超时未支付订单(>15 分钟),若有则自动取消
- 每天凌晨 1 点检查是否有“派送中”订单,若有则自动完成
2.2 代码开发
2.2.1 自定义定时任务类
@Component
@Slf4j
public class OrderTask {
@Autowired
private OrderMapper orderMapper;
@Scheduled(cron = "0 * * * * ?") // 每分钟
public void processTimeoutOrder(){
log.info("处理支付超时订单:{}", new Date());
}
@Scheduled(cron = "0 0 1 * * ?") // 每天凌晨 1 点
public void processDeliveryOrder(){
log.info("处理派送中订单:{}", new Date());
}
}
2.2.2 处理支付超时订单
@Scheduled(cron = "0 * * * * ?")
public void processTimeoutOrder(){
log.info("处理支付超时订单:{}", new Date());
LocalDateTime time = LocalDateTime.now().plusMinutes(-15);
List<Orders> ordersList = orderMapper.getByStatusAndOrdertimeLT(Orders.PENDING_PAYMENT, time);
if(ordersList != null && !ordersList.isEmpty()){
for (Orders orders : ordersList) {
orders.setStatus(Orders.CANCELLED);
orders.setCancelReason("支付超时,自动取消");
orders.setCancelTime(LocalDateTime.now());
orderMapper.update(orders);
}
}
}
2.2.3 处理派送中订单
@Scheduled(cron = "0 0 1 * * ?")
public void processDeliveryOrder(){
log.info("处理派送中订单:{}", new Date());
LocalDateTime time = LocalDateTime.now().plusMinutes(-60);
List<Orders> ordersList = orderMapper.getByStatusAndOrdertimeLT(Orders.DELIVERY_IN_PROGRESS, time);
if(ordersList != null && !ordersList.isEmpty()){
for (Orders orders : ordersList) {
orders.setStatus(Orders.CANCELLED); // 已完成
orderMapper.update(orders);
}
}
}
2.2.4 Mapper 层
OrderMapper.java
@Select("select * from orders where status = #{status} and order_time < #{orderTime}")
List<Orders> getByStatusAndOrdertimeLT(Integer status, LocalDateTime orderTime);
2.3 功能测试
步骤 1:查看订单表
存在状态为 1(待支付)的订单
步骤 2:开启定时任务
启动服务,控制台每分钟输出任务执行日志

步骤 3:再次查看订单表
状态已更新为 6(已取消)
证明定时任务生效。
“派送中”订单处理方式类似,可通过修改cron表达式加快任务频率进行测试。
关于定时任务的功能就基本实现了,下一章小柒味来 - WebSocket 来单提醒
欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 1701220998@qq.com