Implementing scheduling in Spring Boot

In this tutorial, we are going to look at how we can schedule tasks to be executed in the future in spring boot applications.

Spring provides very convenient and painless ways of scheduling tasks. To enable scheduling in your spring boot application, you add @EnableScheduling annotation in any of your configuration files. This annotation does not require any additional dependencies.

@SpringBootApplication 
@EnableScheduling
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}

To schedule tasks, you can use the @Scheduled annotation for simple tasks or use a programmable api for more complex tasks.

The @Scheduled annotation allows one to schedule tasks at a fixed rate, fixed Delay or using cron expressions. You can add an initial delay to both fixed rate and fixed delay. Methods annotated using @Scheduled annotation don’t accept any parameters and return void.

Fixed Rate: — this option is used when you want to execute a task a fixed interval of time. e.g The code below will be executed after every 1 second.

@Scheduled(fixedRate = 1000)     
public void scheduleTaskWithFixedRate(){
logger.info("I run after every second.");
}

The tasks are executed in a single thread, so if u had overlapping tasks, the code will be run after the previous one completes. You can use @Async annotation to run tasks in parallel. You can read more about @Async annotation in this guide. https://spring.io/guides/gs/async-method/

Fixed Delay: — this option is used to run tasks after a fixed period from the execution of the last method. fixedRate specifies the interval between method invocations, measured from the start time of each invocation. fixedDelay specifies the interval between invocations measured from the completion of the task. The code below will run after 1s. Then after it completes, it will be scheduled to run again in 1s time.

@Scheduled(fixedDelay = 1000)     
public void scheduledTaskWithFixeDelay(){

logger.info("I run after 1 second after the completion of the previous task.");
}

Adding an initial delay.

@Scheduled(initialDelay=2000, fixedDelay = 1000)     
public void scheduledTaskWithInitialDelay(){

logger.info("Task with initial delay");
}

The task will be executed a first time after the initialDelay of 2 seconds and then it will continue to be executed according to the fixedDelay of 1 second.

For more complex tasks, for example, you want to run some task at the last Sunday of every month or the first day of the month at 10:15am, cron expressions are more convenient and come in handy.

The cron job below will run at 10:15am at the first of every month.

@Scheduled(cron="0 15 10 1 * *")     
public void scheduleTaskWithCronExpression(){

logger.info("Task Using cron expression");
}

To schedule a task to run on every last Sunday of the month at 2.15PM, u would use the cron expression below.

@Scheduled(cron="0 15 14 ? * 1L *")     
public void scheduleTaskWithCronExpression(){

logger.info("Task using cron expression");
}

Writing cron expressions can be complex and error prone. It is also disastrous since there is no simple way to test. There are good free tools to generate this cron expressions on the web. I personally use https://www.freeformatter.com/cron-expression-generator-quartz.html . There is also https://crontab.guru/. Cron expressions generated by crontab guru have 5 parameters instead of the 6 required by spring cron expression. The seconds part of the cron expression is usually not included as per the time of writing this tutorial. So remember to add it otherwise you will get compile time errors. There is also a good resource to learn about cron expressions by Baeldung at https://www.baeldung.com/cron-expressions.

If you have more complex scheduling operations that can’t be handled the methods we have discussed above, you can schedule tasks programmatically using the TaskScheduler interface provided by spring. This interface has the following methods.

public interface TaskScheduler {

ScheduledFuture schedule(Runnable task, Trigger trigger);

ScheduledFuture schedule(Runnable task, Date startTime);

ScheduledFuture scheduleAtFixedRate(Runnable task, Date startTime, long period);

ScheduledFuture scheduleAtFixedRate(Runnable task, long period);

ScheduledFuture scheduleWithFixedDelay(Runnable task, Date startTime, long delay);

ScheduledFuture scheduleWithFixedDelay(Runnable task, long delay);

}

The simplest method is the one that accepts a Runnable and a date only. Apart from it, the rest of the methods are capable of executing periodic tasks. All the methods provided by this interface can easily be implemented using the @Scheduled annotation and therefore don’t provide much flexibility. However, ScheduledFuture schedule(Runnable task, Trigger trigger) which accepts a Trigger is much more flexible and ideal for complex tasks. Let’s illustrate this using the example below.

Suppose you have a requirement where you want to send a notification to particular group of users to file their report 3 days before the end of every month. Different months have different days making it hard to use cron expression for this task.

Let’s create a class called FileReportNotificationTask that implements Runnable interface and contains the business logic of sending the notification.

Next, lets create a class called AppSchedulerConfigurer where the `magic ` happens. I will explain the code in the next step.

This class is a spring configuration class used to configure scheduling tasks. It implements SchedulingConfigurer interface that has only method, configureTasks that accepts ScheduledTaskRegistrar class that enables us to add trigger tasks. It contains lots of other useful methods for configuring tasks but lets focus on this particular one for our current scenario.

The addTriggerTask(Runnable task, Trigger trigger). This method accepts same parameters as schedule method provided by TaskScheduler. We could have as well used used this method directly as shown here.

@Autowired private TaskScheduler taskScheduler;
taskScheduler.schedule(runnableTask, trigger);

Using addTriggerTask provides us with the advantage that the schedule task will be automatically called by spring and we don’t have to do it ourselves.

In our implementation, we passed FileReportNotificationTask as our runnable task and created an inner class for the Trigger.The runnable has the business logic we would like to execute in the future. The Trigger enables us to define a date in the future when we would like to execute the task. It provides a simple interface which defines one method nextExecutionTime that accepts TriggerContext interface and returns a Java Date when we would like to execute our code

public interface Trigger {
Date nextExecutionTime(TriggerContext triggerContext);
}

The TriggerContext encapsulates all of the relevant data. It provides the following methods to get when we last ran our scheduled task. This is important as we can schedule next execution dates based on last execution dates.

public interface TriggerContext {

Date lastScheduledExecutionTime();

Date lastActualExecutionTime();

Date lastCompletionTime();

}

These methods are self explanatory. In code above, we use lastScheduledExecutionTime() which returns last scheduled execution time of the task or null if not scheduled before

The nextExecutionTime method should return the Date when want to run our scheduled task. In our implementation, if the lastScheduledExecutionTime() is null, we return the third last day of the current month since we don’t want to skip the current month. Otherwise, the next execution time will be the third last day of the next month. Below are the implementations of the two methods to get the next execution dates. We use Java LocalDateTime classes to simply calculating next dates.

If you don’t want the method to execute in the future, you can return null and your runnable task won’t be scheduled.

Hope this case scenario gave you an understanding of how to implement slightly complicated scheduling tasks using the spring TaskScheduler and Triggers. We could as well go on and discuss how to run your scheduled tasks in multiple threads but I will leave this out for another tutorial.

Lastly, if your have more complicated tasks that you would like to persist across server reboots incase your server goes down while running your scheduled tasks or you want to run your scheduled tasks in a cluster of servers, you can consider using Quartz open source scheduling framework for java. It offers great flexibility without sacrificing simplicity and integrates well with spring. Here is a link to their official website http://www.quartz-scheduler.org/

Hope this guide was useful and helpful in understanding how to enable and implement scheduling in spring boot in various ways.

Thanks for taking your time to read this article. Cheers.