Implementing Dynamic Scheduled Tasks in Spring Boot
This guide explains how to create Spring Boot scheduled tasks whose execution intervals can be changed at runtime using configurable cron expressions or periodic triggers, and provides REST endpoints to update the schedule without restarting the application.
Previously I wrote an article about using scheduled tasks in a Spring Boot project, but because the tasks relied on cron expressions defined in configuration files, the execution times could not be changed dynamically while the application was running.
After researching online, I documented how to implement dynamic scheduled tasks in a Spring Boot project.
Since this is only a demo, the required dependencies are limited to the following:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
<optional>true</optional>
</dependency>
<!-- For Spring Boot 2.3 and later, validation must be imported manually -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>The main application class enables scheduling:
package com.wl.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
/**
* @author wl
* @date 2022/3/22
*/
@EnableScheduling
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
System.out.println("(*^▽^*)启动成功!!!(〃'▽'〃)");
}
}The application.yml file only defines the server port:
server:
port: 8089The task execution time is configured in task-config.ini :
printTime.cron=0/10 * * * * ?The scheduled task implementation reads the cron expression and creates a CronTrigger so the interval can be changed at runtime:
package com.wl.demo.task;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.scheduling.Trigger;
import org.springframework.scheduling.TriggerContext;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
import java.util.Date;
/**
* Scheduled task
* @author wl
* @date 2022/3/22
*/
@Data
@Slf4j
@Component
@PropertySource("classpath:/task-config.ini")
public class ScheduleTask implements SchedulingConfigurer {
@Value("${printTime.cron}")
private String cron;
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.addTriggerTask(new Runnable() {
@Override
public void run() {
log.info("Current time: {}", LocalDateTime.now());
}
}, new Trigger() {
@Override
public Date nextExecutionTime(TriggerContext triggerContext) {
// Use CronTrigger, the cron expression can be modified dynamically
CronTrigger cronTrigger = new CronTrigger(cron);
Date nextExecutionTime = cronTrigger.nextExecutionTime(triggerContext);
return nextExecutionTime;
}
});
}
}A REST controller provides an endpoint to update the cron expression on the fly:
package com.wl.demo.controller;
import com.wl.demo.task.ScheduleTask;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author wl
* @date 2022/3/22
*/
@Slf4j
@RestController
@RequestMapping("/test")
public class TestController {
private final ScheduleTask scheduleTask;
@Autowired
public TestController(ScheduleTask scheduleTask) {
this.scheduleTask = scheduleTask;
}
@GetMapping("/updateCron")
public String updateCron(String cron) {
log.info("new cron :{}", cron);
scheduleTask.setCron(cron);
return "ok";
}
}Running the project shows the task executing every 10 seconds. By calling /test/updateCron?cron=0/15 * * * * ? , the interval changes to 15 seconds, which is confirmed by the logs.
Besides the cron‑based approach, a PeriodicTrigger can be used to set an arbitrary millisecond interval, overcoming the limitation that cron expressions cannot define intervals greater than 59 seconds.
package com.wl.demo.task;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.scheduling.Trigger;
import org.springframework.scheduling.TriggerContext;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.scheduling.support.PeriodicTrigger;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
import java.util.Date;
/**
* Scheduled task
* @author wl
* @date 2022/3/22
*/
@Data
@Slf4j
@Component
@PropertySource("classpath:/task-config.ini")
public class ScheduleTask implements SchedulingConfigurer {
@Value("${printTime.cron}")
private String cron;
private Long timer = 10000L; // default 10 seconds
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.addTriggerTask(new Runnable() {
@Override
public void run() {
log.info("Current time: {}", LocalDateTime.now());
}
}, new Trigger() {
@Override
public Date nextExecutionTime(TriggerContext triggerContext) {
// CronTrigger can be used when a cron expression is provided
//CronTrigger cronTrigger = new CronTrigger(cron);
//Date nextExecutionTime = cronTrigger.nextExecutionTime(triggerContext);
// PeriodicTrigger allows setting an arbitrary interval in milliseconds
PeriodicTrigger periodicTrigger = new PeriodicTrigger(timer);
Date nextExecutionTime = periodicTrigger.nextExecutionTime(triggerContext);
return nextExecutionTime;
}
});
}
}An additional endpoint is added to modify the timer value:
package com.wl.demo.controller;
import com.wl.demo.task.ScheduleTask;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author wl
* @date 2022/3/22
*/
@Slf4j
@RestController
@RequestMapping("/test")
public class TestController {
private final ScheduleTask scheduleTask;
@Autowired
public TestController(ScheduleTask scheduleTask) {
this.scheduleTask = scheduleTask;
}
@GetMapping("/updateCron")
public String updateCron(String cron) {
log.info("new cron :{}", cron);
scheduleTask.setCron(cron);
return "ok";
}
@GetMapping("/updateTimer")
public String updateTimer(Long timer) {
log.info("new timer :{}", timer);
scheduleTask.setTimer(timer);
return "ok";
}
}Test results show the task interval changing according to the provided cron expression or timer value.
Java Captain
Focused on Java technologies: SSM, the Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading; occasionally covers DevOps tools like Jenkins, Nexus, Docker, ELK; shares practical tech insights and is dedicated to full‑stack Java development.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.