
本文介绍如何在 Spring Boot 应用中正确配置和运行周期性定时任务,使用 @Scheduled 注解替代原始 Timer/TimerTask 方案,实现每5分钟自动查询数据库并发送提醒邮件。
本文介绍如何在 spring boot 应用中正确配置和运行周期性定时任务,使用 `@scheduled` 注解替代原始 `timer`/`timertask` 方案,实现每5分钟自动查询数据库并发送提醒邮件。
在 Spring Boot 中实现“应用启动后持续运行的定时任务”,最标准、最推荐的方式是使用 Spring 自带的基于注解的调度框架(Spring Scheduling),而非手动管理 java.util.Timer 或 ScheduledExecutorService。后者不仅难以与 Spring 的依赖注入(如 @Autowired)集成,还会导致 Bean 无法正确注入(如你代码中 ScheduledTaskSevenDays 类里的 adminService 始终为 null),进而引发空指针异常。
✅ 正确做法:启用 @Scheduled 并注入 Spring 管理的 Bean
首先,在主启动类上添加 @EnableScheduling 注解以开启定时任务支持:
@SpringBootApplication
@EnableScheduling // ? 关键:启用 Spring 定时任务功能
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
接着,创建一个 Spring Bean(例如 NotificationScheduler),并在其中定义被 @Scheduled 标记的方法。该方法将由 Spring 容器托管,可安全使用 @Autowired 注入服务:
@Component
public class NotificationScheduler {
private final AdminService adminService;
private final Logger logger = LoggerFactory.getLogger(NotificationScheduler.class);
public NotificationScheduler(AdminService adminService) {
this.adminService = adminService;
}
/**
* 每 5 分钟执行一次:查询即将到期(7天内)的参与者并发送邮件提醒
* fixedDelay 表示上一次执行完成后,延迟 5 分钟再执行下一次(推荐用于耗时不确定的任务)
*/
@Scheduled(fixedDelay = 5, timeUnit = TimeUnit.MINUTES)
public void sendSevenDayReminder() {
logger.info("Starting 7-day survey reminder task...");
try {
boolean success = adminService.SevenDayNotification();
if (success) {
logger.info("7-day reminder emails sent successfully.");
} else {
logger.warn("7-day reminder task completed with partial failure.");
}
} catch (Exception e) {
logger.error("Unexpected error in 7-day reminder task", e);
}
}
}
? 提示:fixedDelay 更安全——它确保前一次任务执行完毕后才开始倒计时;若需严格按“每5分钟整点触发”(如每小时 :00、:05、:10…),可改用 cron = "0 0/5 * * * ?"(秒 分 时 日 月 周 年,年可省略)。
⚠️ 注意事项与最佳实践
- Bean 必须由 Spring 管理:@Scheduled 方法所在的类必须是 Spring Bean(如 @Component, @Service, @Configuration),否则注解无效。
-
避免在 @Scheduled 方法中阻塞主线程:Spring 默认使用单线程 TaskScheduler。若多个定时任务耗时长,会相互阻塞。如需并发执行,可自定义线程池:
@Configuration @EnableScheduling public class SchedulerConfig { @Bean public TaskScheduler taskScheduler() { ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler(); scheduler.setPoolSize(5); // 最多5个并发任务 scheduler.setThreadNamePrefix("scheduled-task-"); scheduler.initialize(); return scheduler; } } - 异常处理不可省略:定时任务中未捕获的异常会导致后续调度中断(Spring 默认停止该任务)。务必用 try-catch 包裹核心逻辑,并记录日志。
- 数据库查询 + 邮件发送应具备幂等性:建议在 SevenDayNotification() 方法中增加时间窗口校验(如只查 dueDate BETWEEN NOW() AND NOW() + INTERVAL 7 DAY),并考虑添加发送状态标记(如 notified_at 字段),防止重复发信。
✅ 总结
使用 @Scheduled 是 Spring Boot 应用实现可靠、可维护、可测试定时任务的标准方案。它天然支持依赖注入、事务管理、AOP 增强(如重试、监控),且与 Spring Boot Actuator 集成后还可通过 /actuator/scheduledtasks 端点查看所有定时任务状态。相比手动 Timer,它更轻量、更健壮、更符合 Spring 生态设计哲学。请立即重构你的调度逻辑,拥抱声明式定时任务。










