Spring Task定时任务详解与实战应用
一、Spring Task核心机制
Spring Task是Spring框架内置的轻量级定时任务模块,无需额外依赖即可实现定时任务调度。其核心是TaskScheduler
接口和@Scheduled
注解。
启用步骤:
-
在启动类添加
@EnableScheduling
@SpringBootApplication @EnableScheduling // 激活定时任务功能 public class Application {public static void main(String[] args) {SpringApplication.run(Application.class, args);} }
-
在业务方法添加
@Scheduled
注解
二、@Scheduled参数配置
参数类型 | 示例 | 说明 |
---|---|---|
cron | "0 0 1 * * ?" | UNIX风格时间表达式 |
fixedDelay | fixedDelay=5000 | 上次执行结束到下次开始的间隔 |
fixedRate | fixedRate=3000 | 固定频率执行(毫秒) |
initialDelay | initialDelay=10000 | 首次执行延迟时间(毫秒) |
三、Cron表达式详解
Spring Task使用标准的6位cron表达式(7位会报错):
┌──────────── [秒] (0-59)
│ ┌────────── [分] (0-59)
│ │ ┌──────── [时] (0-23)
│ │ │ ┌────── [日] (1-31)
│ │ │ │ ┌──── [月] (1-12 或 JAN-DEC)
│ │ │ │ │ ┌── [周] (0-7 或 SUN-SAT, 0/7=周日)
│ │ │ │ │ │
* * * * * *
常用表达式示例:
0 * * * * *
→ 每分钟的0秒执行(每分钟一次)
0 0 1 * * ?
→ 每天凌晨1点执行
0 0/5 9-18 * * MON-FRI
→ 工作日9-18点每5分钟执行
四、实战案例解析
场景1:订单超时处理(每分钟执行)
@Scheduled(cron = "0 * * * * *") // 每分钟触发
public void processTimeoutOrders() {LocalDateTime cutoffTime = LocalDateTime.now().minusMinutes(15);List<Orders> orders = orderMapper.selectPendingOrdersBefore(Orders.PENDING_PAYMENT, cutoffTime);orders.forEach(order -> {order.setStatus(Orders.CANCELLED);order.setCancelReason("支付超时自动取消");order.setCancelTime(LocalDateTime.now());orderMapper.update(order);});
}
实现逻辑:检查15分钟未支付的订单→批量更新为取消状态
场景2:自动完成配送订单(每天凌晨执行)
@Scheduled(cron = "0 0 1 * * ?") // 每天1:00 AM执行
public void completeDeliveryOrders() {LocalDateTime cutoffTime = LocalDateTime.now().minusHours(1);List<Orders> orders = orderMapper.selectDeliveryOrdersBefore(Orders.DELIVERY_IN_PROGRESS, cutoffTime);orders.forEach(order -> {order.setStatus(Orders.COMPLETED);order.setCompletionTime(LocalDateTime.now());orderMapper.update(order);});
}
实现逻辑:检索派送中超过1小时的订单→自动标记为已完成
五、最佳实践建议
-
线程池配置(防止任务阻塞)
spring:task:scheduling:pool:size: 10 # 定时任务线程池大小thread-name-prefix: sched-task-
-
分布式环境处理
-
使用Redis分布式锁:
@Scheduled
+RedissonLock
-
数据库悲观锁:
SELECT ... FOR UPDATE
-
异常处理
@Scheduled(...)
public void scheduledTask() {try {// 业务逻辑} catch (Exception e) {log.error("定时任务执行失败", e);// 告警通知}
}