Schedule Tasks Programmatically in Spring Boot
Intro
Scheduling tasks in Spring can easily be done using the @Scheduled
annotation, which offers a great deal of flexibility. However, sometimes you need to schedule tasks programmatically, for example when you want to schedule a task based on user input.
In this tutorial we take a look at how to schedule tasks using the TaskScheduler
interface.
Let’s Code
The TaskScheduler
is the central interface to schedule tasks in Spring. It offers methods for scheduling at a fixed rate, with a fixed delay, or, based on a trigger.
To get the scheduler from the application context, we can simply inject it into our class. But we have to make sure to use @EnableScheduling
since otherwise it will not be available.
package dev.axgr import org.slf4j.LoggerFactoryimport org.springframework.boot.CommandLineRunnerimport org.springframework.boot.autoconfigure.SpringBootApplicationimport org.springframework.boot.runApplicationimport org.springframework.context.annotation.Beanimport org.springframework.scheduling.TaskSchedulerimport org.springframework.scheduling.annotation.EnableSchedulingimport org.springframework.scheduling.support.CronTriggerimport java.time.Durationimport java.util.concurrent.TimeUnit @EnableScheduling@SpringBootApplicationclass App {
companion object { private val log = LoggerFactory.getLogger(App::class.java) } @Bean fun run(scheduler: TaskScheduler) = CommandLineRunner { val one = scheduler.scheduleAtFixedRate({ log.info("Job #1 working..") }, Duration.ofSeconds(1)) Thread.sleep(TimeUnit.SECONDS.toMillis(3)) one.cancel(true) } }
fun main(args: Array<String>) { runApplication<App>(*args)}
With the scheduler at hand, we can schedule a simple logging task that is executed every second. We also get back a refernce to the task, a ScheduledFuture
, which we can use to cancel the task after about 3 seconds.
Scheduling a task with a fixed delay works pretty mucht the same; so let’s take a look at scheduling a task using a cron expression.
package dev.axgr import org.slf4j.LoggerFactoryimport org.springframework.boot.CommandLineRunnerimport org.springframework.boot.autoconfigure.SpringBootApplicationimport org.springframework.boot.runApplicationimport org.springframework.context.annotation.Beanimport org.springframework.scheduling.TaskSchedulerimport org.springframework.scheduling.annotation.EnableSchedulingimport org.springframework.scheduling.support.CronTriggerimport java.time.Durationimport java.util.concurrent.TimeUnit @EnableScheduling@SpringBootApplicationclass App {
companion object { private val log = LoggerFactory.getLogger(App::class.java) } @Bean fun run(scheduler: TaskScheduler) = CommandLineRunner { val one = scheduler.scheduleAtFixedRate({ log.info("Job #1 working..") }, Duration.ofSeconds(1)) Thread.sleep(TimeUnit.SECONDS.toMillis(3)) one.cancel(true) val cron = CronTrigger("* * * * * *") val two = scheduler.schedule({ log.info("Job #2 working..") }, cron) Thread.sleep(TimeUnit.SECONDS.toMillis(3)) two?.cancel(true) } }
fun main(args: Array<String>) { runApplication<App>(*args)}
The schedule
method takes the runnable and an implementation of the Trigger
interface. That interface is used to calculate the next execution time. One such implementation is the CronTrigger
which can be initialized using a String.
If we want the job to run every second, we can pass * * * * * *
as the cron pattern.
This starts the job and again, after about 3 seconds we cancel that job as well. Note that the reference to the second task may be null, in case the trigger never fires. So we have to use a safe call for the cancellation.