Here is the translated article body:
Do you have any tasks in Spring Boot that you want to run on a regular schedule — such as log rotation or data backups?
The @Scheduled annotation is exactly what you need. While Spring offers batch processing frameworks like Spring Batch, the @Scheduled annotation is the convenient choice for simple periodic tasks.
This article explains how to use the @Scheduled annotation with concrete code examples.
What is the @Scheduled Annotation?
The @Scheduled annotation is a feature provided by the Spring Framework that executes a method according to a specified schedule. No additional libraries are required, so it is easy to get started.
All you need to do is define a class with the @EnableScheduling annotation enabled.
This is required so that Spring Boot can detect and manage scheduled tasks.
How to Use It
Consider the following class as an example.
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
@Component
@EnableScheduling
public class ScheduledTask {
@Scheduled(fixedRate = 5000) // 5秒ごとに実行
public void reportCurrentTime() {
System.out.println("現在時刻: " + LocalDateTime.now());
}
@Scheduled(cron = "0 0 * * * *") // 毎時0分に実行
public void executeEveryHour() {
System.out.println("毎時0分に実行されるタスクです。");
}
}
This code defines a ScheduledTask class managed by the Spring container via the @Component annotation.
The @EnableScheduling annotation enables the scheduled task functionality.
The reportCurrentTime() method is executed every 5 seconds by @Scheduled(fixedRate = 5000). The fixedRate attribute specifies the time in milliseconds between the start of one method execution and the start of the next.
The executeEveryHour() method is executed at minute 0 of every hour by @Scheduled(cron = "0 0 * * * *"). The cron attribute uses a cron expression, which allows for more flexible scheduling. A cron expression is specified in the order: seconds, minutes, hours, day-of-month, month, day-of-week. "0 0 * * * *" means “execute at second 0 of minute 0 of every hour.”
For detailed cron expression syntax, there are many articles and handy online tools available — it’s worth looking them up.
Other Attributes
In addition to the above, the @Scheduled annotation has attributes such as fixedDelay and initialDelay, which allow for more fine-grained scheduling. fixedDelay specifies the time in milliseconds between the completion of the previous execution and the start of the next — unlike fixedRate, it accounts for the method’s execution time. initialDelay specifies the delay in milliseconds before the first execution.
There is also a zone attribute for specifying a timezone. By default, the system’s default timezone is used, but you can specify one as needed.
If you want to ensure the task always runs on Japan Standard Time, configure it like this:
@Scheduled(cron = "0 0 * * * *", zone = "Asia/Tokyo")
Common Pitfalls in Production
While @Scheduled is easy to use, there are some traps to watch out for in production.
1. Exceptions Can Go Unnoticed
When an exception occurs inside a scheduled task, it may only appear in the logs and be missed.
At a minimum, the following practices will help stabilize operations:
- Log the start and end of each execution
- Write stack traces to the error log on failure
- Send notifications (Slack/email) if needed
2. Tasks Back Up When Processing Time Exceeds Execution Interval
If fixedRate is set too short, tasks can pile up and put pressure on resources when the processing can’t keep up.
For long-running tasks, it is safer to use fixedDelay or make the processing asynchronous with a queue.
3. Duplicate Execution Across Multiple Instances
When running multiple instances on Kubernetes or similar platforms, the same @Scheduled task runs once per instance.
For tasks that should only run once, combine them with a DB lock or a distributed lock (e.g., ShedLock).
Here is an example of how to prevent duplicate execution:
@Scheduled(cron = "0 */10 * * * *", zone = "Asia/Tokyo")
@SchedulerLock(name = "dailyAggregateTask", lockAtMostFor = "PT5M", lockAtLeastFor = "PT30S")
public void aggregateDailyMetrics() {
// aggregation logic
}
Choosing Between fixedRate, fixedDelay, and cron
When in doubt, the following guidelines will help you make the right choice:
fixedRate: For measurement-type tasks that should run at a fixed intervalfixedDelay: For heavier tasks where you want a gap after the previous execution completescron: For tasks that should align with business hours, such as “3:00 AM daily” or “9:00 AM on weekdays”
With scheduled tasks, operational quality depends less on when it runs and more on how it recovers from failure.
It is recommended to define your retry and notification strategies at the initial implementation stage.
Stabilizing Operations with an Explicit Thread Pool
@Scheduled works with its default configuration, but as the number of tasks grows, contention becomes more likely.
In production, it is safer to explicitly define a dedicated thread pool for the scheduler.
@Configuration
@EnableScheduling
public class SchedulingConfig {
@Bean
public TaskScheduler taskScheduler() {
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setPoolSize(4);
scheduler.setThreadNamePrefix("scheduled-task-");
scheduler.setWaitForTasksToCompleteOnShutdown(true);
scheduler.setAwaitTerminationSeconds(30);
return scheduler;
}
}
Tune poolSize according to the number of tasks and their execution times, and configure the scheduler to avoid interruptions during shutdown — this reduces operational incidents.
How to Think About Testing Scheduled Tasks
Waiting for @Scheduled to trigger during a test takes too long, so the following approach is more practical:
- Extract the business logic into a regular method, separate from the schedule
- In tests, call that regular method directly
- Keep validation of the scheduling configuration to a minimum (e.g., a startup check)
Separating “execution logic” from “when to execute” makes both testing and maintenance much easier.
Summary
The @Scheduled annotation is a powerful tool for concisely describing periodic task execution. Once you master cron expressions, you can handle a wide variety of schedules.
Give it a try in your own application.
And don’t forget to add error handling and logging as needed — aim to build a robust system.