Jobs executing tasks periodically are common in computer systems. You may want to do something every minute, hour or make it running always on Monday 8 am. There are a few different solutions to achieve this. As I come from the database world, I would consider database solutions in the first place, but they are not the best for systems were business logic is implemented on the Java side and the periodic task is a part of that logic. So this time I will show you how to do it with Spring scheduler.
Spring Context package
The solution I am describing here requires Spring Context library. If you use Gradle, build.gradle in your project should contain spring-context package as a compile dependency.
dependencies {
...
compile group: 'org.springframework', name: 'spring-context', version: '4.3.9.RELEASE'
...
}
or Spring Boot Starter Web is also fine.
dependencies {
...
compile group: 'org.springframework.boot', name: 'spring-boot-starter-web', version: '1.5.4.RELEASE'
...
}
It adds more libraries but if you are using Spring Boot anyway, it is more convenient.
Do not forget to refresh Gradle configuration to load newly added libraries.
Enabling scheduling
It is not enough to add a package. Scheduling has to be explicitly enabled by @EnableScheduling annotation on the class with the main method.
@SpringBootApplication
@EnableScheduling
public class Application extends SpringBootServletInitializer {
public static void main(String[] args) {
...
}
}
Method to test scheduling with Spring
For the demonstration purposes I created a class with one method that prints a message with the current date and time.
@Component
public class TestSchedulingClass {
protected void runPeriodically() {
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = new Date();
System.out.println("Ran at " + dateFormat.format(date));
}
}
Notice that it is annotated with @Component to make it managed by Spring. It has to be somehow instantiated once so Spring could run the runPeriodically method on schedule. Of course with the current code it will not be executed as it has not bee scheduled, yet. @Service, @Controller annotations would work as well as they would create an object from TestSchedulingClass but in my case it is neither a service nor a controller so @Component works best.
Scheduling examples - rate
There are a few ways of scheduling tasks. The first one is based on a rate. You can define a number of milliseconds between task executions.
@Scheduled(fixedRate = 1000L)
protected void runPeriodically() {
…
}
The above example executes the method every 1000 milliseconds (1 second). A sample output looks as below:
Ran at 2017-07-03 21:05:56
Ran at 2017-07-03 21:05:57
Ran at 2017-07-03 21:05:58
Ran at 2017-07-03 21:05:59
Ran at 2017-07-03 21:06:00
Scheduling examples - delay
The second option may look similar to the one based on a rate but a delay is a time between the end of the previous execution and the beginning of the next one. A sample usage:
@Scheduled(fixedDelay = 1000L)
protected void runPeriodically() {
…
}
Result is similar as this method is very quick.
Ran at 2017-07-03 21:16:12
Ran at 2017-07-03 21:16:13
Ran at 2017-07-03 21:16:14
Ran at 2017-07-03 21:16:15
Scheduling examples - cron
The third option is the most powerful. If you are familiar with unix cron, you do not need more explanation. In case you do, a schedule is defined by six components:
- Seconds.
- Minutes.
- Hours.
- Day in a month.
- Month.
- Weekday.
This formula makes the method executed at 2 am every Saturday.
@Scheduled(cron = "0 0 2 * * SAT")
protected void runPeriodically() {
…
}
More examples:
0 * * * * * - every minute (each time when the seconds part is 0)
0 0 * * * * - every hour (each time when the seconds part and the minutes part is 0)
0 2 16,17 * * * - at 16:02 (4:02 pm) and 17:02 (5:02 pm) each day
*/30 * * * * * - every half a minute
0 0 3 * * MON-FRI - at 3:00 am on weekdays
0 0 0 1 1 * - on January 1st at midnight