Spring @Scheduled 注解使用详解
目录
- 概述
- 基本配置
- @Scheduled 参数详解
- 高级用法
- 异常处理
- 实际应用示例
- 注意事项
- 总结
概述
@Scheduled 是 Spring Framework 中用于任务调度的注解,可以方便地实现定时任务功能。它基于 Spring 的任务调度抽象,支持多种调度方式。
基本配置
启用定时任务
配置类方式:
@Configuration
@EnableScheduling
public class SchedulingConfig {
// 配置类
}
Spring Boot 主类方式:
@SpringBootApplication
@EnableScheduling
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
@Scheduled 参数详解
1. cron 表达式
@Component
public class ScheduledTasks {
/**
* cron 表达式格式: [秒] [分] [时] [日] [月] [周] [年] (年可选)
*/
@Scheduled(cron = "0 * * * * ?") // 每分钟执行一次
public void taskWithCron() {
System.out.println("执行定时任务: " + new Date());
}
// 每天上午10:15执行
@Scheduled(cron = "0 15 10 * * ?")
public void dailyTask() {
// 业务逻辑
}
// 每周一上午9点执行
@Scheduled(cron = "0 0 9 ? * MON")
public void weeklyTask() {
// 业务逻辑
}
}
2. fixedRate
@Component
public class FixedRateTasks {
/**
* fixedRate: 固定速率执行
* 从上一次开始时间计算下一次开始时间
*/
@Scheduled(fixedRate = 5000) // 每5秒执行一次
public void taskWithFixedRate() {
System.out.println("FixedRate任务执行: " + new Date());
}
// 结合时间单位
@Scheduled(fixedRate = 2, timeUnit = TimeUnit.HOURS)
public void taskWithTimeUnit() {
// 每2小时执行一次
}
}
3. fixedDelay
@Component
public class FixedDelayTasks {
/**
* fixedDelay: 固定延迟执行
* 从上一次完成时间计算下一次开始时间
*/
@Scheduled(fixedDelay = 3000) // 上次执行完成后3秒再执行
public void taskWithFixedDelay() {
try {
Thread.sleep(1000); // 模拟任务执行时间
System.out.println("FixedDelay任务执行: " + new Date());
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
4. initialDelay
@Component
public class InitialDelayTasks {
/**
* initialDelay: 初始延迟
* 应用启动后延迟指定时间开始执行第一次任务
*/
@Scheduled(initialDelay = 10000, fixedRate = 5000)
public void taskWithInitialDelay() {
System.out.println("带初始延迟的任务执行: " + new Date());
}
}
高级用法
1. 从配置文件读取参数
@Component
public class ConfigurableScheduledTasks {
@Scheduled(cron = "${task.cron.expression:0 0/5 * * * ?}")
public void configurableTask() {
System.out.println("可配置的定时任务: " + new Date());
}
@Scheduled(fixedRateString = "${task.fixed.rate:5000}")
public void configurableFixedRateTask() {
System.out.println("可配置的固定速率任务: " + new Date());
}
}
配置文件 application.properties:
task.cron.expression=0 */10 * * * ?
task.fixed.rate=10000
配置文件 application.yml:
task:
cron:
expression: "0 */10 * * * ?"
fixed:
rate: 10000
2. 使用条件判断
@Component
@ConditionalOnProperty(name = "scheduling.enabled", havingValue = "true")
public class ConditionalScheduledTask {
@Scheduled(fixedRate = 5000)
public void conditionalTask() {
System.out.println("条件定时任务执行: " + new Date());
}
}
3. 异步执行
@Component
@EnableAsync
public class AsyncScheduledTasks {
@Async
@Scheduled(fixedRate = 5000)
public void asyncTask() {
System.out.println("异步定时任务开始: " + Thread.currentThread().getName());
try {
Thread.sleep(3000); // 模拟耗时操作
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("异步定时任务结束: " + Thread.currentThread().getName());
}
}
异常处理
1. 基本的异常处理
@Component
public class ExceptionHandlingTasks {
@Scheduled(fixedRate = 5000)
public void taskWithExceptionHandling() {
try {
// 业务逻辑
System.out.println("任务执行: " + new Date());
// 模拟可能出现的异常
if (new Random().nextBoolean()) {
throw new RuntimeException("模拟异常");
}
} catch (Exception e) {
System.err.println("任务执行异常: " + e.getMessage());
// 记录日志、发送告警等
}
}
}
2. 使用自定义任务调度器
@Configuration
public class SchedulerConfig {
@Bean
public TaskScheduler taskScheduler() {
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setPoolSize(10);
scheduler.setThreadNamePrefix("scheduled-task-");
scheduler.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
scheduler.setWaitForTasksToCompleteOnShutdown(true);
scheduler.setAwaitTerminationSeconds(60);
return scheduler;
}
}
实际应用示例
1. 数据清理任务
@Component
public class DataCleanupTask {
private final DataCleanupService dataCleanupService;
public DataCleanupTask(DataCleanupService dataCleanupService) {
this.dataCleanupService = dataCleanupService;
}
/**
* 每天凌晨2点清理30天前的数据
*/
@Scheduled(cron = "0 0 2 * * ?")
public void cleanupOldData() {
try {
System.out.println("开始数据清理任务: " + new Date());
dataCleanupService.cleanupDataOlderThan(30);
System.out.println("数据清理任务完成: " + new Date());
} catch (Exception e) {
System.err.println("数据清理任务失败: " + e.getMessage());
// 发送告警通知
}
}
}
2. 缓存刷新任务
@Component
public class CacheRefreshTask {
private final CacheService cacheService;
public CacheRefreshTask(CacheService cacheService) {
this.cacheService = cacheService;
}
/**
* 每5分钟刷新缓存
*/
@Scheduled(fixedRate = 5 * 60 * 1000)
public void refreshCache() {
System.out.println("刷新缓存: " + new Date());
cacheService.refreshAllCaches();
}
}
3. 健康检查任务
@Component
public class HealthCheckTask {
private final HealthCheckService healthCheckService;
private final NotificationService notificationService;
public HealthCheckTask(HealthCheckService healthCheckService,
NotificationService notificationService) {
this.healthCheckService = healthCheckService;
this.notificationService = notificationService;
}
/**
* 每30秒执行健康检查
*/
@Scheduled(fixedRate = 30000)
public void healthCheck() {
HealthStatus status = healthCheckService.checkSystemHealth();
if (!status.isHealthy()) {
notificationService.sendAlert("系统健康检查失败: " + status.getMessage());
}
}
}
注意事项
1. 单线程问题
默认情况下,Spring 的 @Scheduled 使用单线程执行所有定时任务。如果任务执行时间较长,可能会影响其他任务的执行。
解决方案:
@Configuration
@EnableScheduling
public class SchedulingConfig implements SchedulingConfigurer {
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.setScheduler(taskExecutor());
}
@Bean(destroyMethod = "shutdown")
public Executor taskExecutor() {
return Executors.newScheduledThreadPool(10);
}
}
2. 集群环境下的重复执行
在集群环境中,需要确保定时任务不会被多个实例重复执行。
解决方案:
- 使用分布式锁(Redis、Zookeeper等)
- 使用数据库悲观锁
- 使用 Quartz 集群模式
3. 任务执行时间监控
@Component
public class MonitoredScheduledTask {
@Scheduled(fixedRate = 5000)
public void monitoredTask() {
long startTime = System.currentTimeMillis();
try {
// 业务逻辑
System.out.println("监控任务执行: " + new Date());
} finally {
long executionTime = System.currentTimeMillis() - startTime;
if (executionTime > 3000) { // 如果执行时间超过3秒
System.err.println("任务执行时间过长: " + executionTime + "ms");
}
}
}
}
总结
@Scheduled 注解提供了强劲而灵活的定时任务功能,通过合理配置可以满足大部分定时任务需求。在使用时需要注意:
- 合理选择调度策略(cron、fixedRate、fixedDelay)
- 思考任务执行时间和线程池配置
- 在集群环境中处理任务重复执行问题
- 添加适当的异常处理和监控机制
通过合理使用 @Scheduled 注解,可以构建出稳定可靠的定时任务系统。
© 版权声明
文章版权归作者所有,未经允许请勿转载。如内容涉嫌侵权,请在本页底部进入<联系我们>进行举报投诉!
THE END














暂无评论内容