Spring Boot
Scheduler
Java
Task Scheduling
Debugging

Scheduler not running in Spring Boot

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

When a scheduled method does not run in Spring Boot, the cause is usually one of a few predictable issues: scheduling was never enabled, the class is not a Spring-managed bean, the trigger never matches, or the scheduler thread is blocked. The fastest way to debug it is to reduce the problem to a minimal working scheduled method and add evidence with logs.

Start with the Smallest Working Example

A scheduled method only runs if scheduling is enabled and the method belongs to a Spring bean.

java
1import org.springframework.boot.SpringApplication;
2import org.springframework.boot.autoconfigure.SpringBootApplication;
3import org.springframework.scheduling.annotation.EnableScheduling;
4import org.springframework.scheduling.annotation.Scheduled;
5import org.springframework.stereotype.Component;
6
7@SpringBootApplication
8@EnableScheduling
9public class DemoApplication {
10    public static void main(String[] args) {
11        SpringApplication.run(DemoApplication.class, args);
12    }
13}
14
15@Component
16class HeartbeatJob {
17    @Scheduled(fixedRate = 5000)
18    public void run() {
19        System.out.println("heartbeat");
20    }
21}

If @EnableScheduling is missing, the method never runs. If the class is not a bean, Spring never sees the annotation.

Make Sure Spring Owns the Class

@Scheduled works through Spring bean management. That means the annotated class must be discovered by component scanning or created through configuration.

java
1import org.springframework.context.annotation.Bean;
2import org.springframework.context.annotation.Configuration;
3import org.springframework.scheduling.annotation.Scheduled;
4
5@Configuration
6class JobConfig {
7    @Bean
8    CleanupJob cleanupJob() {
9        return new CleanupJob();
10    }
11}
12
13class CleanupJob {
14    @Scheduled(fixedDelay = 10000)
15    public void clean() {
16        System.out.println("cleanup");
17    }
18}

This works because Spring creates the bean. By contrast, calling new CleanupJob() yourself elsewhere in the code creates a plain Java object that the scheduler ignores.

Verify the Trigger Type

A scheduler that seems idle may actually be waiting on a trigger you did not mean to configure. fixedRate, fixedDelay, and cron behave differently.

java
1import org.springframework.scheduling.annotation.Scheduled;
2import org.springframework.stereotype.Component;
3
4@Component
5class ReportJob {
6    @Scheduled(cron = "0 */5 * * * *", zone = "UTC")
7    public void generate() {
8        System.out.println("report");
9    }
10}

In Spring, cron expressions include a seconds field. That catches many developers who copy a five-field Unix cron expression and expect it to work unchanged.

If you are debugging, temporarily switch to a short fixedRate schedule so you can observe behavior quickly.

Add Logging Around the Method

Do not debug this by staring at the code and waiting. Add explicit logs.

java
1import java.time.Instant;
2import org.slf4j.Logger;
3import org.slf4j.LoggerFactory;
4import org.springframework.scheduling.annotation.Scheduled;
5import org.springframework.stereotype.Component;
6
7@Component
8class InventoryJob {
9    private static final Logger log = LoggerFactory.getLogger(InventoryJob.class);
10
11    @Scheduled(fixedDelay = 15000)
12    public void sync() {
13        log.info("Inventory sync started at {}", Instant.now());
14    }
15}

If no log appears, the problem is likely registration or configuration. If logs appear later than expected, the method may be blocked or waiting on thread capacity.

Long-Running Jobs Can Block the Default Scheduler

A job that takes a long time can make it look as if the scheduler stopped. If multiple tasks share too few scheduler threads, one slow method can delay the others.

java
1import org.springframework.context.annotation.Bean;
2import org.springframework.context.annotation.Configuration;
3import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
4
5@Configuration
6class SchedulerConfig {
7    @Bean
8    ThreadPoolTaskScheduler taskScheduler() {
9        ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
10        scheduler.setPoolSize(4);
11        scheduler.setThreadNamePrefix("sched-");
12        scheduler.initialize();
13        return scheduler;
14    }
15}

This becomes important when scheduled work performs network calls, file I/O, or long database operations.

Watch for Self-Inflicted Design Problems

A scheduled method should stay small and observable. If it contains a large chain of blocking work with no logs and no timing information, it becomes hard to tell whether the scheduler failed or whether the job ran once and got stuck.

As a practical debugging sequence:

  1. confirm @EnableScheduling
  2. confirm the class is a Spring bean
  3. replace the real trigger with a short fixedRate
  4. log entry and exit
  5. check thread-pool behavior if the job is slow

This sequence usually isolates the issue quickly.

Common Pitfalls

A common mistake is forgetting @EnableScheduling. Without it, @Scheduled has no effect.

Another is placing the scheduled method on a class that Spring does not manage. The annotation is only meaningful on a bean.

Cron syntax also causes confusion, especially because Spring uses a seconds field. A copied expression from another scheduler may never fire at the expected time.

Long-running tasks can make the scheduler appear broken when the real issue is thread starvation.

Summary

  • Enable scheduling with @EnableScheduling.
  • Put @Scheduled methods on Spring-managed beans.
  • Use the correct trigger type and remember Spring cron includes seconds.
  • Add logs so you can prove whether the method is firing.
  • Configure a scheduler thread pool when jobs can block or overlap.

Course illustration
Course illustration

All Rights Reserved.