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,且不能在同类内部调用(会绕过代理,导致异步失效)
- ✅ 返回值建议为
void
或Future
/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