Scheduling Tasks in Spring Boot

2 minute read

Run tasks on a schedule — clean up expired tokens every hour, send daily reports, sync data every 5 minutes.


Setup

@SpringBootApplication
@EnableScheduling
public class BookstoreApiApplication { }

@Scheduled Options

@Service
public class ScheduledTasks {

    // Every 5 seconds
    @Scheduled(fixedRate = 5000)
    public void runEvery5Seconds() {
        log.info("Fixed rate task: {}", LocalDateTime.now());
    }

    // 10 seconds after previous execution finishes
    @Scheduled(fixedDelay = 10000)
    public void runWithDelay() {
        log.info("Fixed delay task: {}", LocalDateTime.now());
    }

    // Initial delay + fixed rate
    @Scheduled(initialDelay = 5000, fixedRate = 30000)
    public void runAfterStartup() {
        log.info("Started 5s after boot, runs every 30s");
    }

    // Cron expression: every day at 2:00 AM
    @Scheduled(cron = "0 0 2 * * *")
    public void dailyCleanup() {
        log.info("Daily cleanup at 2 AM");
        tokenService.deleteExpiredTokens();
    }
}
Parameter Behavior
fixedRate Runs at constant intervals (even if previous is still running)
fixedDelay Waits for previous to finish + delay
cron Unix-like cron expression

Cron Expressions

┌───────────── second (0-59)
│ ┌───────────── minute (0-59)
│ │ ┌───────────── hour (0-23)
│ │ │ ┌───────────── day of month (1-31)
│ │ │ │ ┌───────────── month (1-12)
│ │ │ │ │ ┌───────────── day of week (0-7, 0=Sun)
│ │ │ │ │ │
* * * * * *
Expression Schedule
0 0 * * * * Every hour
0 0 2 * * * Daily at 2:00 AM
0 0 9-17 * * MON-FRI Every hour 9 AM–5 PM weekdays
0 */15 * * * * Every 15 minutes
0 0 0 1 * * First day of every month

Real-World Examples

@Service
public class BookstoreScheduler {

    private final TokenRepository tokenRepository;
    private final ReportService reportService;
    private final CacheManager cacheManager;

    // Clean expired tokens every hour
    @Scheduled(cron = "0 0 * * * *")
    public void cleanExpiredTokens() {
        int deleted = tokenRepository.deleteExpiredBefore(Instant.now());
        log.info("Cleaned {} expired tokens", deleted);
    }

    // Send daily sales report at 8 AM
    @Scheduled(cron = "0 0 8 * * MON-FRI")
    public void sendDailySalesReport() {
        reportService.generateAndSendDailyReport();
    }

    // Warm cache every 30 minutes
    @Scheduled(fixedRate = 1800000)
    public void warmCache() {
        cacheManager.getCache("popularBooks").clear();
        bookService.getPopularBooks();  // repopulates cache
    }
}

ShedLock — Distributed Locking

When running multiple instances, you don’t want the same task running on all of them:

<dependency>
    <groupId>net.javacrumbs.shedlock</groupId>
    <artifactId>shedlock-spring</artifactId>
    <version>5.12.0</version>
</dependency>
<dependency>
    <groupId>net.javacrumbs.shedlock</groupId>
    <artifactId>shedlock-provider-jdbc-template</artifactId>
    <version>5.12.0</version>
</dependency>
@Configuration
@EnableSchedulerLock(defaultLockAtMostFor = "10m")
public class SchedulerConfig {

    @Bean
    public LockProvider lockProvider(DataSource dataSource) {
        return new JdbcTemplateLockProvider(dataSource);
    }
}
@Scheduled(cron = "0 0 2 * * *")
@SchedulerLock(name = "dailyCleanup", lockAtLeastFor = "5m", lockAtMostFor = "30m")
public void dailyCleanup() {
    // Only ONE instance runs this — others skip
    tokenService.deleteExpiredTokens();
}

Key Takeaways

  1. @EnableScheduling + @Scheduled = background task scheduling.
  2. fixedRate for constant intervals; fixedDelay waits for completion.
  3. Cron expressions for complex schedules (daily, weekdays, monthly).
  4. ShedLock prevents duplicate execution across multiple instances.
  5. Scheduled tasks should be idempotent — safe to run multiple times.

What’s Next?

Next: Application Events — decoupling components with Spring events.

Updated: