一、描述
在 Spring Boot 中设置动态定时任务是一种非常实用的功能,可以根据实际需求在运行时动态地调整定时任务的执行时间、频率等参数。以下是对 Spring Boot 设置动态定时任务的简单介绍:
1、传统定时任务的局限性
在传统的 Spring Boot 定时任务中,通常是通过在方法上使用@Scheduled
注解来设置固定的执行时间和频率。这种方式在任务的执行计划相对固定的情况下是有效的,但在一些需要动态调整任务执行时间的场景中就显得不够灵活。例如,在一个电商系统中,可能需要根据不同的促销活动时间动态地调整订单处理任务的执行时间;在一个监控系统中,可能需要根据实时的系统负载情况动态地调整数据采集任务的执行频率。
2、动态定时任务的实现方式
-
使用数据库存储任务配置信息:
- 可以在数据库中创建一个任务配置表,存储任务的名称、执行时间、执行频率等信息。
- 在 Spring Boot 应用启动时,读取数据库中的任务配置信息,并根据这些信息创建定时任务。
- 当需要动态调整任务执行时间时,可以通过修改数据库中的任务配置信息,然后通知 Spring Boot 应用重新读取任务配置,从而实现动态调整定时任务的目的。
-
使用配置文件动态加载:
- 在 Spring Boot 的配置文件(如 application.properties 或 application.yml)中,可以定义一些动态的配置参数,用于设置定时任务的执行时间和频率。
- 通过使用 Spring Boot 的配置文件加载机制,可以在运行时动态地修改配置文件中的参数,然后通知 Spring Boot 应用重新加载配置文件,从而实现动态调整定时任务的目的。
-
使用管理界面进行动态配置:
- 可以开发一个管理界面,让管理员可以在界面上直接设置定时任务的执行时间和频率。
- 管理界面可以通过调用 Spring Boot 应用提供的 API 来修改任务配置信息,然后通知应用重新读取任务配置,从而实现动态调整定时任务的目的。
3、动态定时任务的优势
- 灵活性高:可以根据实际需求在运行时动态地调整定时任务的执行时间和频率,满足不同场景下的需求。
- 易于管理:通过数据库、配置文件或管理界面等方式,可以方便地管理定时任务的配置信息,提高系统的可维护性。
- 实时响应:可以根据实时的业务需求和系统状态动态地调整定时任务,提高系统的响应能力和效率。
二、案例
1、第一种写法
1、新建spring boot项目
添加相应的依赖
<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>
<!-- spring boot 2.3版本后,如果需要使用校验,需手动导入validation包-->
<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>
2、启动类
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
/**
* @author wl
*/
@EnableScheduling
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
System.out.println("(*^▽^*)启动成功!!!(〃'▽'〃)");
}
}
3、配置文件application.yml
定义服务端口
server:
port: 8089
定时任务执行时间配置文件:task-config.ini:
myPrintTime.cron=0/10 * * * * ?
4、定时任务执行类:
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:/my-task-config.ini")
public class CustomScheduleTask implements SchedulingConfigurer {
@Value("${myPrintTime.cron}")
private String cron;
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.addTriggerTask(() -> {
log.info("Custom task executed at: {}", LocalDateTime.now());
}, new Trigger() {
@Override
public Date nextExecutionTime(TriggerContext triggerContext) {
// 使用CronTrigger触发器,可动态修改cron表达式来操作循环规则
CronTrigger cronTrigger = new CronTrigger(cron);
Date nextExecutionTime = cronTrigger.nextExecutionTime(triggerContext);
return nextExecutionTime;
}
});
}
}
5、controller
编写一个接口,构建动态定时任务管理接口
import com.wl.demo.task.ScheduleTask;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
@Slf4j
@RestController
@RequestMapping("/test")
public class TestController {
private final ScheduleTask scheduleTask;
@Autowired
public TestController(ScheduleTask scheduleTask) {
this.scheduleTask = scheduleTask;
}
@GetMapping("/updateCron")
public ResponseEntity<String> updateCron(String cron) {
log.info("new cron :{}", cron);
scheduleTask.setCron(cron);
return new ResponseEntity<>("定时任务 cron 表达式更新成功", HttpStatus.OK);
}
}
启动项目,可以看到任务每10秒执行一次
也可以通过url地址修改任务时间,localhost:8089ytest/updateCron?cron=0/15 * * * *?
6、CronTrigger触发器
除了上面的借助cron表达式的方法,还有另一种触发器,区别于CronTrigger触发器,该触发器可随意设置循环间隔时间,不像cron表达式只能定义小于等于间隔59秒,在这个新的定时任务类中,我们可以从不同的配置文件中读取 cron 表达式,并且设置了一个不同的默认循环时间。根据实际需求,可以灵活地调整 cron 表达式和循环时间间隔。
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;
@Data
@Slf4j
@Component
@PropertySource("classpath:/my-task-config.ini")
public class NewScheduleTask implements SchedulingConfigurer {
@Value("${myPrintTime.cron}")
private String cron;
private Long customTimer = 15000L;
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.addTriggerTask(() -> {
log.info("New task execution time: {}", LocalDateTime.now());
}, new Trigger() {
@Override
public Date nextExecutionTime(TriggerContext triggerContext) {
// 使用 CronTrigger 或根据需求切换
CronTrigger cronTrigger = new CronTrigger(cron);
Date nextExecutionTime = cronTrigger.nextExecutionTime(triggerContext);
if (nextExecutionTime == null) {
PeriodicTrigger periodicTrigger = new PeriodicTrigger(customTimer);
return periodicTrigger.nextExecutionTime(triggerContext);
}
return nextExecutionTime;
}
});
}
}
修改时间的接口
import com.wl.demo.task.ScheduleTask;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
@Slf4j
@RestController
@RequestMapping("/test")
public class TestController {
private final ScheduleTask scheduleTask;
@Autowired
public TestController(ScheduleTask scheduleTask) {
this.scheduleTask = scheduleTask;
}
@GetMapping("/updateCron")
public ResponseEntity<String> updateCron(String cron) {
log.info("new cron :{}", cron);
scheduleTask.setCron(cron);
return new ResponseEntity<>("定时任务 cron 表达式更新成功", HttpStatus.OK);
}
@GetMapping("/updateTimer")
public String updateTimer(Long timer) {
log.info("new timer :{}", timer);
scheduleTask.setTimer(timer);
return "ok";
}
}
运行结果
2、第二种写法
1、添加依赖
和前面一样,在 Maven 项目的pom.xml
文件中添加以下依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
2、创建定时任务执行接口和实现类
首先定义一个定时任务执行的接口:
public interface ScheduledTaskExecutor {
void execute();
}
然后创建一个实现类:
import org.springframework.stereotype.Component;
@Component
public class DynamicTaskExecutor implements ScheduledTaskExecutor {
@Override
public void execute() {
System.out.println("定时任务执行:" + new java.util.Date());
}
}
3、配置定时任务调度器
创建一个定时任务调度器类
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.Trigger;
import org.springframework.scheduling.TriggerContext;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.scheduling.support.CronTrigger;
import java.util.Date;
@Configuration
@EnableScheduling
public class ScheduledTaskConfig implements SchedulingConfigurer {
@Autowired
private DynamicTaskExecutor dynamicTaskExecutor;
private String cronExpression = "0/5 * * * *?"; // 默认每 5 秒执行一次
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.addTriggerTask(
() -> dynamicTaskExecutor.execute(),
triggerContext -> {
CronTrigger trigger = new CronTrigger(cronExpression);
return trigger.nextExecutionTime(triggerContext);
}
);
}
// 提供一个方法用于动态修改定时任务的表达式
public void setCronExpression(String expression) {
cronExpression = expression;
}
}
4、动态修改定时任务
可以在其他地方调用ScheduledTaskConfig
的setCronExpression()
方法来动态修改定时任务的执行时间表达式。
例如:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class SchedulingService {
@Autowired
private ScheduledTaskConfig scheduledTaskConfig;
public void changeCronExpression(String newExpression) {
scheduledTaskConfig.setCronExpression(newExpression);
}
}
通过这种方式,你可以在运行时动态地调整定时任务的执行时间,提高应用的灵活性。