Spring @Scheduled注解使用详解

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 注解提供了强劲而灵活的定时任务功能,通过合理配置可以满足大部分定时任务需求。在使用时需要注意:

  1. 合理选择调度策略(cron、fixedRate、fixedDelay)
  2. 思考任务执行时间和线程池配置
  3. 在集群环境中处理任务重复执行问题
  4. 添加适当的异常处理和监控机制

通过合理使用 @Scheduled 注解,可以构建出稳定可靠的定时任务系统。

© 版权声明
THE END
如果内容对您有所帮助,就支持一下吧!
点赞0 分享
王六六岁的头像 - 鹿快
评论 抢沙发

请登录后发表评论

    暂无评论内容