Skip to content

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-SAT1=周日(与 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 下前次未跑完)、保证幂等(重复执行无副作用)、捕获异常(本轮异常不影响下一轮,但会刷日志)。

以「技术栈」为核心的个人知识库