Backend Development 10 min read

Implementing Dynamic Scheduled Tasks in Spring Boot

This article demonstrates how to create Spring Boot scheduled tasks whose execution intervals can be changed at runtime using configurable cron expressions or a PeriodicTrigger, providing full source code, configuration files, and a REST API for updating the schedule.

Java Architect Essentials
Java Architect Essentials
Java Architect Essentials
Implementing Dynamic Scheduled Tasks in Spring Boot

Previously, scheduled tasks in Spring Boot required static cron expressions defined in configuration files, making runtime adjustments impossible. This guide shows how to implement dynamic scheduling that can be modified while the application is running.

Dependencies – Only the essential Spring Boot starters and Lombok are required:

<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>
    
    <!-- validation for Spring Boot 2.3+ -->
    <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>

Application entry point enables scheduling:

package com.wl.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;

@EnableScheduling
@SpringBootApplication
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
        System.out.println("(*^▽^*)启动成功!!!(〃'▽'〃)");
    }
}

The server port is defined in application.yml :

server:
  port: 8089

Task configuration is stored in task-config.ini so the cron expression can be changed without rebuilding the jar:

printTime.cron=0/10 * * * * ?

Dynamic task implementation (CronTrigger version) reads the cron value from the properties file and registers a trigger that uses the current expression each time it fires:

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;

@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(() -> {
            log.info("Current time: {}", LocalDateTime.now());
        }, new Trigger() {
            @Override
            public Date nextExecutionTime(TriggerContext triggerContext) {
                CronTrigger cronTrigger = new CronTrigger(cron);
                return cronTrigger.nextExecutionTime(triggerContext);
            }
        });
    }
}

A simple REST controller allows the cron expression to be updated at runtime:

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;

@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 application shows the task executing every 10 seconds. After calling /test/updateCron?cron=0/15 * * * * ? , the interval changes to 15 seconds.

Alternative using PeriodicTrigger – when the required interval exceeds the 59‑second limit of cron, a PeriodicTrigger can be used. The task class is extended with a timer field (milliseconds) and the trigger returns a PeriodicTrigger instance:

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.PeriodicTrigger;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.util.Date;

@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(() -> {
            log.info("Current time: {}", LocalDateTime.now());
        }, new Trigger() {
            @Override
            public Date nextExecutionTime(TriggerContext triggerContext) {
                // PeriodicTrigger allows any millisecond interval
                PeriodicTrigger periodicTrigger = new PeriodicTrigger(timer);
                return periodicTrigger.nextExecutionTime(triggerContext);
            }
        });
    }
}

The controller is expanded with an endpoint to modify the timer value:

@GetMapping("/updateTimer")
public String updateTimer(Long timer) {
    log.info("new timer :{}", timer);
    scheduleTask.setTimer(timer);
    return "ok";
}

Testing shows the task interval changing according to the supplied timer, demonstrating full runtime configurability of Spring Boot scheduled jobs.

The article concludes with a call to join a community of Java architects and provides links to additional resources and source code packages.

BackendJavaSpringBootCronDynamicSchedulingPeriodicTrigger
Java Architect Essentials
Written by

Java Architect Essentials

Committed to sharing quality articles and tutorials to help Java programmers progress from junior to mid-level to senior architect. We curate high-quality learning resources, interview questions, videos, and projects from across the internet to help you systematically improve your Java architecture skills. Follow and reply '1024' to get Java programming resources. Learn together, grow together.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.