spring环境下的动态定时任务研究
0.前言
传统spring定时任务采用的是@Sechedu
注解去实现,但是该注解只能指定固定的时间任务,例如:配置了2s执行一次,那么永远只能是每两秒执行一次
但是在有些特殊场景下需要一些动态定时任务,例如:最初配置了2s执行一次,在执行任务中,修改配置为5秒执行一次,那么就需要动态的加载配置,使任务动态的变成5s执行一次
1.原理
要想实现动态定时任务,就需要借助Spring
的SchedulingConfigurer
接口,该方式可以实现动态时间,定时任务,具体原理如下图:
时间不一定要存数据库里面,也可以存配置文件或者其他地方,这里数据库只是举了个例子
具体还是要看业务背景,毕竟脱离业务背景单纯搞技术只是纸上谈兵
2.实现
2.0 新建项目
新建一个springboot项目,具体步骤这个不再赘述,其pom.xml
内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.2.RELEASE</version>
</parent>
<groupId>org.example</groupId>
<artifactId>task-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.2</version>
</dependency>
</dependencies>
</project>
2.1 配置Cron
在数据库中新建表并插入一条数据,如下图所示:
新建pojo
和映射接口以及service
,如下:
-
pojo
package com.tomato.bean; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; @Data @TableName("cron") public class Cron { @TableId(type = IdType.AUTO) private Integer id; private String cron; }
-
Mapper
package com.tomato.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.tomato.bean.Cron; public interface CronMapper extends BaseMapper<Cron> { }
-
service
package com.tomato.service; import com.tomato.bean.Cron; import com.tomato.mapper.CronMapper; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.Optional; @Service @Slf4j public class CronService { @Autowired private CronMapper cronMapper; public String getCron() { // 只是为了测试 固定id Cron cron = cronMapper.selectById(1); return cron.getCron(); } }
-
application.yml
2.2 配置解释
2.2.1 SchedulingConfigurer
要想实现定时任务主要是实现SchedulingConfigurer
接口,然后重写configureTasks
方法。
从源码可知该方法参数为ScheduledTaskRegistrar
类型,该参数就是用来注册要执行的定时任务,如下图所示:
2.2.2 ScheduledTaskRegistrar
在configureTasks
方法中,ScheduledTaskRegistrar
通过addTriggerTask
来添加触发器任务,从而去执行。
而addTriggerTask
方法有两个参数,一个是要执行得任务,一个是触发器,如下图:
其实从上图源码中可以知道,调用该方法其本质是构建一个
TriggerTask
任务,然后添加进去
其中该方法参数如下:
task 代表要执行得任务,一个
Runable
类型-
trigger 触发器,代表在什么时候触发执行任务,该接口有两个实现类,如下:
其中
CronTrigger
触发器代表cron
表达式得触发器,其源码如下:从源码可知创建该触发器需要传入一个
cron
表达式
2.3 配置代码
具体配置代码如下:
package com.tomato.config;
import com.tomato.bean.Cron;
import com.tomato.service.CronService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.scheduling.support.CronTrigger;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.atomic.AtomicInteger;
@Configuration
@Slf4j
public class SchedulingTaskConfigurer implements SchedulingConfigurer {
@Autowired
private CronService cronService;
private final AtomicInteger number = new AtomicInteger(1);
/**
* 该方法就是用来实现定时任务
* @param taskRegistrar
*/
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.addTriggerTask(() -> {
log.info("测试任务:{},时间:{}" , number.addAndGet(1), LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
}, triggerContext -> {
String expression = cronService.getCron();
log.info("b:cron:{}", expression);
// nextExecutionTime 表示下一次任务执行时间
return new CronTrigger(expression).nextExecutionTime(triggerContext);
});
}
}
2.4 启动类代码
在启动类上加上开启定时任务注解
@EnableScheduling
package com.tomato;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
@SpringBootApplication
@EnableScheduling
@MapperScan("com.tomato.mapper")
public class TaskApplication {
public static void main(String[] args) {
SpringApplication.run(TaskApplication.class, args);
}
}
3.测试
这样就完成一个动态定时任务配置