1. 启用定时任务

在Spring Boot主类上添加 @EnableScheduling 注解:

@SpringBootApplication
@EnableScheduling
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

2. 基本定时任务

2.1 使用 @Scheduled 注解

@Component
public class ScheduledTasks {
    
    private static final Logger log = LoggerFactory.getLogger(ScheduledTasks.class);
    
    // 固定延迟执行 - 上一次执行完成后延迟5秒再执行
    @Scheduled(fixedDelay = 5000)
    public void fixedDelayTask() {
        log.info("固定延迟任务执行: {}", new Date());
    }
    
    // 固定频率执行 - 每5秒执行一次
    @Scheduled(fixedRate = 5000)
    public void fixedRateTask() {
        log.info("固定频率任务执行: {}", new Date());
    }
    
    // 使用Cron表达式 - 每分钟执行一次
    @Scheduled(cron = "0 * * * * ?")
    public void cronTask() {
        log.info("Cron任务执行: {}", new Date());
    }
}

3. Cron表达式详解

3.1 Cron表达式格式

秒 分 时 日 月 周 [年]

3.2 常用Cron表达式示例

// 每秒执行一次
@Scheduled(cron = "0/1 * * * * ?")

// 每分钟执行一次
@Scheduled(cron = "0 * * * * ?")

// 每小时执行一次
@Scheduled(cron = "0 0 * * * ?")

// 每天凌晨1点执行
@Scheduled(cron = "0 0 1 * * ?")

// 每周一凌晨1点执行
@Scheduled(cron = "0 0 1 ? * MON")

// 每月1号凌晨1点执行
@Scheduled(cron = "0 0 1 1 * ?")

// 工作日上午9点到下午5点,每半小时执行一次
@Scheduled(cron = "0 0/30 9-17 ? * MON-FRI")

3.3 特殊字符说明

  • *:匹配所有值
  • ?:不指定值(用于日和周,避免冲突)
  • -:范围(如:1-5)
  • ,:列举多个值(如:1,3,5)
  • /:增量(如:0/5表示从0开始每5个单位)
  • L:最后(如:月的最后一天、周的最后一天)
  • W:工作日
  • #:第几个(如:2#3表示第3个星期二)

4. 高级配置

4.1 配置文件中定义Cron表达式

# application.yml
schedule:
  tasks:
    backup: "0 0 2 * * ?"  # 每天凌晨2点执行备份
    cleanup: "0 0 3 * * ?" # 每天凌晨3点执行清理
@Component
public class ConfigurableScheduledTasks {
    
    @Value("${schedule.tasks.backup}")
    private String backupCron;
    
    @Value("${schedule.tasks.cleanup}")
    private String cleanupCron;
    
    @Scheduled(cron = "${schedule.tasks.backup}")
    public void backupTask() {
        // 备份逻辑
    }
    
    @Scheduled(cron = "${schedule.tasks.cleanup}")
    public void cleanupTask() {
        // 清理逻辑
    }
}

4.2 异步定时任务

@Configuration
@EnableAsync
public class AsyncConfig {
    
    @Bean
    public TaskExecutor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(5);
        executor.setMaxPoolSize(10);
        executor.setQueueCapacity(25);
        executor.setThreadNamePrefix("AsyncTask-");
        executor.initialize();
        return executor;
    }
}

@Component
public class AsyncScheduledTasks {
    
    @Async
    @Scheduled(fixedRate = 5000)
    public void asyncTask() {
        // 异步执行的任务
    }
}

4.3 条件执行定时任务

@Component
public class ConditionalScheduledTasks {
    
    @Value("${app.scheduled.enabled:true}")
    private boolean scheduledEnabled;
    
    @Scheduled(fixedRate = 5000)
    public void conditionalTask() {
        if (!scheduledEnabled) {
            return;
        }
        // 任务逻辑
    }
}

5. 动态定时任务

5.1 使用 TaskScheduler

@Component
public class DynamicScheduledTasks {
    
    @Autowired
    private TaskScheduler taskScheduler;
    
    private ScheduledFuture<?> scheduledFuture;
    
    public void startDynamicTask(Runnable task, String cronExpression) {
        stopDynamicTask();
        scheduledFuture = taskScheduler.schedule(task, 
            new CronTrigger(cronExpression));
    }
    
    public void stopDynamicTask() {
        if (scheduledFuture != null) {
            scheduledFuture.cancel(false);
        }
    }
}

5.2 动态修改Cron表达式

@Component
public class DynamicCronTasks {
    
    private String cronExpression = "0 * * * * ?";
    
    @Scheduled(fixedRate = 10000)
    public void dynamicCronTask() {
        // 可以动态修改cronExpression
    }
    
    public void updateCronExpression(String newCron) {
        this.cronExpression = newCron;
    }
}

6. 定时任务监控

6.1 任务执行监控

@Component
public class MonitoredScheduledTasks {
    
    private static final Logger log = LoggerFactory.getLogger(MonitoredScheduledTasks.class);
    
    @Scheduled(fixedRate = 5000)
    public void monitoredTask() {
        long startTime = System.currentTimeMillis();
        try {
            // 任务逻辑
            log.info("任务执行成功");
        } catch (Exception e) {
            log.error("任务执行失败", e);
        } finally {
            long endTime = System.currentTimeMillis();
            log.info("任务执行耗时: {}ms", endTime - startTime);
        }
    }
}

6.2 使用 Actuator 监控

# application.yml
management:
  endpoints:
    web:
      exposure:
        include: health,info,scheduledtasks

访问 /actuator/scheduledtasks 可以查看所有定时任务信息。

7. 最佳实践

7.1 异常处理

@Component
public class RobustScheduledTasks {
    
    private static final Logger log = LoggerFactory.getLogger(RobustScheduledTasks.class);
    
    @Scheduled(fixedRate = 5000)
    public void robustTask() {
        try {
            // 任务逻辑
        } catch (Exception e) {
            log.error("定时任务执行异常", e);
            // 发送告警通知
        }
    }
}

7.2 避免任务重叠

@Component
public class NonOverlappingTasks {
    
    private volatile boolean isRunning = false;
    
    @Scheduled(fixedRate = 5000)
    public void nonOverlappingTask() {
        if (isRunning) {
            log.warn("上一次任务尚未完成,跳过本次执行");
            return;
        }
        
        isRunning = true;
        try {
            // 任务逻辑
        } finally {
            isRunning = false;
        }
    }
}

7.3 使用分布式锁

@Component
public class DistributedScheduledTasks {
    
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    
    @Scheduled(fixedRate = 5000)
    public void distributedTask() {
        String lockKey = "scheduled:task:lock";
        Boolean locked = redisTemplate.opsForValue()
            .setIfAbsent(lockKey, "locked", Duration.ofMinutes(5));
        
        if (Boolean.TRUE.equals(locked)) {
            try {
                // 任务逻辑
            } finally {
                redisTemplate.delete(lockKey);
            }
        }
    }
}

8. 常见问题

8.1 定时任务不执行

  • 检查是否添加了 @EnableScheduling 注解
  • 确认任务类是否被Spring管理(@Component等注解)
  • 检查Cron表达式是否正确

8.2 任务执行时间不准确

  • 系统时间可能不准确
  • 服务器负载过高导致延迟
  • 任务执行时间过长影响下次执行

8.3 内存泄漏

  • 长时间运行的定时任务要注意资源释放
  • 避免在定时任务中创建大量对象
  • 及时关闭数据库连接等资源

9. 总结

Spring Boot的定时任务功能强大且易于使用,通过 @Scheduled 注解可以快速实现各种定时需求。在实际应用中,需要注意异常处理、任务重叠、分布式环境下的执行等问题,确保定时任务的稳定性和可靠性。