In many applications, we need to run background jobs — like sending reminders, syncing data, or cleaning logs. Spring Boot provides a very simple way to schedule such tasks using the @Scheduled
annotation.
🚀 Enable Scheduling in Spring Boot
Just add @EnableScheduling
in your main class:
@SpringBootApplication
@EnableScheduling
public class CodingBootApplication {
public static void main(String[] args) {
SpringApplication.run(CodingBootApplication.class, args);
}
}
🛠️ Example 1: Run Task Every 10 Seconds
@Component
public class MyScheduler {
@Scheduled(fixedRate = 10000) // runs every 10 sec
public void printMessage() {
System.out.println("Running scheduled task at " + LocalDateTime.now());
}
}
fixedRate = 10000
→ runs the task every 10 seconds after the previous start
🕒 Example 2: Run Task Daily at 2 AM (Using Cron)
@Component
public class DailyJobScheduler {
@Scheduled(cron = "0 0 2 * * *")
public void runDailyJob() {
System.out.println("Running daily cleanup at 2 AM");
}
}
📌 Cron Format in Spring (6 fields):
second minute hour day-of-month month day-of-week
Example:
0 0 2 * * *
→ at 2:00:00 AM every day
second
: 0–59minute
: 0–59hour
: 0–23day-of-month
: 1–31month
: 1–12 (or JAN–DEC)day-of-week
: 0–6 (or SUN–SAT), where 0 = Sunday
🔁 Common Cron Examples:
- Every 5 minutes:
0 */5 * * * *
- Every 30 minutes:
0 */30 * * * *
- Every hour at 15th minute:
0 15 * * * *
- Every day at midnight:
0 0 0 * * *
- Every Monday at 8:30 AM:
0 30 8 * * MON
🧹 Example 3: Cleanup Task Every Midnight
@Component
public class CleanupScheduler {
@Scheduled(cron = "0 0 0 * * ?")
public void clearTempFiles() {
// pretend to delete temp files
System.out.println("[Midnight Cleanup] Temp files cleared");
}
}
📦 YAML-Based Control (Recommended)
Instead of hardcoding cron in code, read it from application.yml
:
scheduler:
cleanup:
cron: "0 0 0 * * ?"
Now use:
@Component
@RequiredArgsConstructor
public class ConfigurableScheduler {
private final Environment environment;
@Scheduled(cron = "#{'${scheduler.cleanup.cron}'}")
public void run() {
System.out.println("[YAML Scheduler] Running at configured time");
}
}
🔄 fixedDelay vs fixedRate vs Async in spring boot scheduler jobs
Attribute | Behavior |
---|---|
fixedRate | Runs every X ms from start time |
fixedDelay | Runs every X ms after previous completes |
@Scheduled(fixedDelay = 5000) // runs after 5 sec of last execution end
💡 What happens if your task takes long time?
Let’s say your task takes 15 seconds to complete.
- With
fixedRate = 10000 // we can also write 10_000 for readability
(10 sec):- The second execution will start even if the previous one is not finished.
- This may result in overlapping executions if the task is slow.
- With
fixedDelay = 10000 // we can also write 10_000 for readability
:- It will wait for the current task to finish.
- Then wait 10 sec, then run again.
- This is safer for long-running jobs.
- With
cron
expression:- Cron triggers the task at the exact specified time, even if the previous task is still running.
- So if your job takes longer than the cron interval, you may get multiple overlapping executions.
- Spring does not stop or queue it. You must manually handle overlaps if needed.
- With
@Async
:- If you annotate your scheduled method with
@Async
, it runs in a separate thread. - This is useful when you don’t want the main thread to wait.
- But it also means your job runs in parallel, so you need to handle overlaps or shared resource issues manually.
@Async @Scheduled(fixedRate = 10000) public void runAsyncJob() { // runs asynchronously every 10 seconds }
- Also add
@EnableAsync
in your main config class.
@SpringBootApplication @EnableAsync @EnableScheduling public class AppMain {}
- If you annotate your scheduled method with
🔁 What Happens in Multiple App Instances?
If your Spring Boot app is running in a cluster or has multiple pods/instances, then:
- Each instance will execute the scheduled task independently.
- That means if 3 instances are running, your task will run 3 times at same time.
💡 To avoid duplicate runs in such setup:
- Use distributed locks (like Redis-based lock or DB-based lock)
- Example: ShedLock — popular lib to manage single-instance execution
⚠️ Common Mistakes in spring boot schedulers job
- ❌ Forgetting
@EnableScheduling
- ❌ Using
@Scheduled
onprivate
methods (must bepublic
) - ❌ Cron format mismatch (watch day-of-week and month)
- ❌ Not handling overlaps in multi-instance deployments
❗ Error Handling in Spring boot Schedulers job
If a scheduled method throws an exception and it’s not caught, it may silently fail or stop future executions in some thread pools.
To prevent this, always wrap your logic inside a try-catch block:
@Scheduled(fixedDelay = 10000)
public void runTask() {
try {
// your business logic
} catch (Exception e) {
log.error("Scheduled job failed", e);
// optionally send alert/email/slack notification
}
}
🧪 Testing Scheduled Jobs
- You can reduce the delay time in
@Scheduled
while testing locally (like 2-3 seconds). - If your job depends on DB or services, consider mocking them to isolate logic.
- Unit testing can be done by extracting the logic into a separate service method.
🚫 Disabling or Controlling Jobs via Config
Sometimes you don’t want a scheduler to run in a certain environment (like staging or test). You can control it from application.yml
:
scheduler:
enabled: false
Use this with @ConditionalOnProperty
:
@Component
@ConditionalOnProperty(name = "scheduler.enabled", havingValue = "true")
public class MyConditionalJob {
@Scheduled(cron = "0 0 1 * * *")
public void run() {
System.out.println("Running only if enabled=true in config");
}
}
📊 Monitoring Spring boot Schedulers job
If you’re running jobs in production, it’s good to send trace logs to tools like:
- 📈 Splunk
- 📊 Kibana
- 📬 Email or Slack (for alerts)
You can also use MDC
or logging frameworks to include trace IDs:
MDC.put("traceId", UUID.randomUUID().toString());
log.info("Job started");
This helps in tracking logs when multiple jobs are running.
📚 Reference
✅ Summary
- Use
@Scheduled
for simple job scheduling in Spring Boot - Understand difference between
fixedRate
,fixedDelay
, andcron
- If job runs long, prefer
fixedDelay
- If deploying on multiple servers, ensure one instance runs the job using locking (like ShedLock)
- YAML-based cron expressions give you config flexibility
Leave a Reply