1. 概述

当单线程处理无法满足需求时,我们可以借助 Spring 提供的异步与定时任务能力。Spring 在 org.springframework.scheduling.annotation 包中提供了一系列注解,用于简化异步执行和任务调度的开发。

本文将系统梳理这些核心注解的使用方式和注意事项,帮你避开常见“坑点”,提升任务调度代码的可维护性和健壮性。


2. @EnableAsync

该注解用于开启 Spring 的异步方法执行支持。它是 @Async 注解生效的前提。

⚠️ 必须配合 @Configuration 使用,通常放在配置类上:

@Configuration
@EnableAsync
class VehicleFactoryConfig {}

✅ 开启后,你就可以在任意 Bean 的方法上使用 @Async,让其在独立线程中执行。

❌ 常见踩坑:只加 @Async 不加 @EnableAsync,方法仍会同步执行,毫无效果。


3. @EnableScheduling

顾名思义,这个注解用于启用 Spring 的定时任务功能

同样,它也需要标注在配置类上:

@Configuration
@EnableScheduling
class VehicleFactoryConfig {}

✅ 一旦启用,你就可以使用 @Scheduled 来定义周期性执行的任务。

⚠️ 注意:Spring Boot 项目中,只要引入了 spring-boot-starter-quartz 或相关依赖,通常建议显式添加此注解以明确意图,避免配置隐式依赖。


4. @Async

这是实现异步调用的核心注解。被它标记的方法会在独立线程中执行,不阻塞主线程。

基本用法

@Async
void repairCar() {
    // 汽车维修逻辑,耗时操作
    Thread.sleep(5000);
    System.out.println("Car repaired asynchronously.");
}

调用 repairCar() 时,主线程不会等待,立即返回,真正执行在任务线程池中。

类级别使用

@Service
@Async
class RepairService {
    void repairCar() { /* ... */ }
    void paintCar() { /* ... */ }
}

此时该类中所有方法默认都异步执行(不推荐,粒度太粗,容易误用)。

注意事项

  • ✅ 必须先启用 @EnableAsync
  • ✅ 方法必须是 public,且不能在同类内部调用(会绕过代理,导致异步失效)
  • ✅ 返回值建议为 voidFuture/CompletableFuture,便于结果获取
  • ❌ 私有方法、final 方法、static 方法无法被代理,@Async 失效

更深入的异步机制(如自定义线程池、异常处理)可参考 Spring Async 完全指南


5. @Scheduled

用于定义定时执行的任务,支持固定频率、固定延迟和 cron 表达式。

常用属性

属性 说明
fixedRate 上次开始后,隔多久再次开始(周期性)
fixedDelay 上次结束后,隔多久再次开始
initialDelay 首次执行前的延迟时间
cron 使用 cron 表达式定义执行时间

示例代码

@Scheduled(fixedRate = 10000)
void checkVehicle() {
    System.out.println("每10秒检查一次车辆状态");
}
@Scheduled(cron = "0 0 9 * * MON-FRI")
void sendDailyReport() {
    System.out.println("工作日早上9点发送日报");
}

多规则支持(Java 8+)

得益于 Java 8 的重复注解特性,一个方法可绑定多个调度规则:

@Scheduled(fixedRate = 10000)
@Scheduled(cron = "0 * * * * MON-FRI")
void checkVehicle() {
    // 每10秒一次,且工作日每分钟一次
}

重要限制

  • ✅ 方法必须是 void 返回类型
  • ✅ 必须启用 @EnableScheduling
  • ✅ 方法应为 public
  • ❌ 不支持方法参数(无法传参)

更多高级用法(如动态调度、条件触发)可参考 Spring 定时任务进阶


6. @Schedules

这是一个容器注解,用于包含多个 @Scheduled 注解。在 Java 8 之前是实现多规则调度的唯一方式。

@Schedules({ 
  @Scheduled(fixedRate = 10000), 
  @Scheduled(cron = "0 * * * * MON-FRI")
})
void checkVehicle() {
    // ...
}

✅ 但在 Java 8+ 环境下,推荐直接使用重复 @Scheduled 注解,更简洁直观。

⚠️ 注意:@Schedules 本身已逐渐被重复注解机制取代,新项目无需特意使用。


7. 总结

注解 作用 必要前置
@EnableAsync 开启异步支持 ——
@Async 标记异步方法 @EnableAsync
@EnableScheduling 开启定时任务 ——
@Scheduled 定义定时任务 @EnableScheduling
@Schedules 容纳多个 @Scheduled(旧方式) 同上

✅ 实际项目中,异步与定时任务常结合使用,例如:

  • 定时任务中触发异步处理(避免阻塞调度线程)
  • 异步任务完成后触发后续操作

所有示例代码均可在 GitHub 获取:https://github.com/eugenp/tutorials/tree/master/spring-boot-modules/spring-boot-annotations


原始标题:Spring 中任务调度相关注解