外观
SpringTask 学习笔记
Spring Task 是 Spring 内置的轻量级定时任务框架,无需额外依赖即可通过注解快速实现定时调度。本文按学习路径整理复习要点:框架认识、cron 表达式、入门项目、自定义定时任务。
1. 了解定时任务框架
- 核心定位:Spring 自带的定时任务方案(spring-context 内置),零额外依赖,适合单机应用。
- 对比 Quartz:Spring Task 轻量简单、够用即止;Quartz 功能重(集群、持久化、丰富触发器),只有复杂调度需求才值得引入。
- 三个关键角色:
@EnableScheduling:开启定时任务支持(放在配置类上)。@Scheduled:标注方法,声明这是一个定时任务。TaskScheduler/TaskExecutor:底层负责调度与执行的线程组件。
- 原理一句话:启动时容器扫描所有
@Scheduled方法并注册为调度任务,由调度线程在约定时间触发执行。
java
@Configuration
@EnableScheduling // 开启定时任务
public class ScheduleConfig {
}2. cron 表达式书写
- Spring cron 固定 6 段:秒 分 时 日 月 周,没有年份段(Unix crontab 是 5 段、Quartz 是 7 段含年,别混用)。
| 字段 | 取值范围 | 说明 |
|---|---|---|
| 秒 | 0-59 | 必填,Spring 特有,最易漏 |
| 分 | 0-59 | |
| 时 | 0-23 | |
| 日 | 1-31 | 与"周"同时限定按"或"匹配 |
| 月 | 1-12 或 JAN-DEC | |
| 周 | 1-7 或 SUN-SAT | 1=周日(与 java.util.Calendar 一致) |
- 常用特殊字符:
*任意值;?不指定值(日/周字段常用);-区间;,枚举;/步长;L/W/#(最后一天/最近工作日/第几个星期几,需 Spring 5.3+,即 Spring Boot 2.5+)。
java
// 每天 12:00 0 0 12 * * ?
// 每分钟(第 0 秒) 0 * * * * ?
// 每 5 分钟 0 */5 * * * ?
// 每天 8:00-10:00 每小时 0 0 8-10 * * ?
// 每月 1 号 3:30 0 30 3 1 * ?
// 周一至周五 9:00 0 0 9 ? * MON-FRI- 易错点:
- 秒段必须写,漏写会导致整体错位(最常见错误)。
- 周字段 1 = 周日,不是周一;建议用英文缩写避免歧义。
- "日"与"周"同限定时是"或"关系(Unix 风格),与 Quartz 必须用
?二选一不同;想表达"同时满足"只能拆成两个任务。
3. 入门项目学习
- 三步上手:加
@EnableScheduling→ 在组件方法上标@Scheduled→ 运行。 - 三种触发方式(可搭配
initialDelay设置首次延迟):cron:表达式触发,最灵活。fixedRate:固定频率,按上次开始时间计时,任务跑太久下轮会立即补跑。fixedDelay:固定延迟,等上次执行结束后再等这么久,天然避免重叠。
java
@Component
public class ReportTask {
@Scheduled(cron = "0 0 12 * * ?")
public void dailyReport() {
// 每天 12:00 执行
}
@Scheduled(fixedRate = 60_000)
public void heartBeat() {
// 每 60 秒执行一次(按开始时间计)
}
@Scheduled(initialDelay = 5_000, fixedDelay = 10_000)
public void afterPrevDone() {
// 启动 5 秒后首次执行,之后每次结束后 10 秒再执行
}
}- 易错点:
- 方法必须无参、返回 void,所在类必须是 Spring Bean;否则不生效。
- 默认调度线程池是单线程,一个长任务会阻塞其他任务,多任务建议配置线程池。
4. 自定义定时任务
- 配置调度线程池(Spring Boot yaml,最省事):
yaml
spring:
task:
scheduling:
pool:
size: 4 # 调度线程数
thread-name-prefix: my-task-- 动态 cron(支持改配置不重启):
@Scheduled的 cron 在启动时固定,运行时无法修改;需要动态变更时改用SchedulingConfigurer+CronTrigger:
java
@Configuration
@EnableScheduling
public class DynamicScheduleConfig implements SchedulingConfigurer {
@Value("${task.cron}")
private String cron;
@Bean
public TaskExecutor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(4);
executor.setThreadNamePrefix("my-task-");
executor.initialize();
return executor;
}
@Override
public void configureTasks(ScheduledTaskRegistrar registrar) {
registrar.setScheduler(taskExecutor());
registrar.addTriggerTask(
() -> doReport(), // 任务逻辑
triggerContext -> new CronTrigger(cron)
.nextExecutionTime(triggerContext) // 每次执行前重新解析 cron
);
}
}- 易错点:
- 线程池别盲目开大:定时任务通常低频,默认单线程多数场景够用,多任务高频才加。
- cron 默认按服务器时区解析,跨时区部署用
@Scheduled(timezone = "Asia/Shanghai")显式指定。 - 任务内做重活要防重入(
fixedRate下前次未跑完)、保证幂等(重复执行无副作用)、捕获异常(本轮异常不影响下一轮,但会刷日志)。