前言
我们通常在设定了定时任务的频率之后,上线运行之后,有一天我们想要改变这个频率,通常的做法是修改代码,然后发布上线,这对于一个稍微有自我要求的程序员来说显然是不可接受的。那么有没有办法在springboot中实现动态改变这个频率呢,答案显然是有的。此处附上spring官方文档。
策略
我们可以把这个频率(比如con表达式)存储在数据库中,然后去读取这个表达式,从而动态的改变频率。我这里使用的是mysql数据库
依赖
<dependency><!--添加Mybatis依赖 配置mybatis的一些初始化的东西-->
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.1</version>
</dependency>
<dependency><!-- 添加mybatis依赖 -->
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.5</version>
<scope>compile</scope>
</dependency>
<dependency><!--添加MySql依赖 -->
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
数据库设计
CREATE TABLE `cron` (
`id` int primary key auto_increment,
`cron` varchar(30) NOT NULL,
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
插入两条数据
- 第一条数据用来实现动态cron表达式的方式
- 第二条数据用来实现固定间隔周期的方式
代码部分
package cn.juhe;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import org.springframework.beans.factory.annotation.Autowired;
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 org.springframework.scheduling.support.PeriodicTrigger;
import org.springframework.util.StringUtils;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.concurrent.TimeUnit;
/**
* @Author: micro cloud fly
* @Description: 可以动态的从数据库修改定时任务的crontab表达式
* @Date: Created in 2:10 下午 2020/11/25
*/
@Configuration
@EnableScheduling
public class DynamicScheduleTask implements SchedulingConfigurer {
@Mapper
public interface CronMapper {
@Select("select cron from cron where id=2")
public String getCron();
}
@Autowired //注入mapper
@SuppressWarnings("all")
CronMapper cronMapper;
/**
* 配置定时任务.
*/
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.addTriggerTask(
//1.第一个参数是一个实现Runnable接口的实现类
new MyTask(),
//2.第二个参数是一个trigger,trigge是一个接口,里面只有一个方法,就是返回一个Date
// new MyCronTrigger()
new MyPeriodicTrigger()
);
}
/**
* cron表达式的实现方式
*/
private class MyCronTrigger implements Trigger{
@Override
public Date nextExecutionTime(TriggerContext triggerContext) {
return new CronTrigger(cronMapper.getCron()).nextExecutionTime(triggerContext);
}
}
/**
* 固定频率的实现方式
*/
private class MyPeriodicTrigger implements Trigger{
@Override
public Date nextExecutionTime(TriggerContext triggerContext) {
return new PeriodicTrigger(Long.parseLong(cronMapper.getCron()), TimeUnit.MILLISECONDS).nextExecutionTime(triggerContext);
}
}
/**
* 自身的业务实现类
*/
private class MyTask implements Runnable{
@Override
public void run() {
System.out.println("开始执行我的定时任务: " + LocalDateTime.now());
}
}
}
补充
通过源码可以发现,Trigger接口有两个默认的实现
因此,我的代码里面实现了两种方式。
附录,spring官方文档
@Target(value=TYPE)
@Retention(value=RUNTIME)
@Import(value=SchedulingConfiguration.class)
@Documented
public @interface EnableScheduling
Enables Spring’s scheduled task execution capability, similar to functionality found in Spring’s task:* XML namespace. To be used on @Configuration classes as follows:
@Configuration
@EnableScheduling
public class AppConfig {
// various @Bean definitions
}
This enables detection of @Scheduled
annotations on any Spring-managed bean in the container. For example, given a class MyTask
package com.myco.tasks;
public class MyTask {
@Scheduled(fixedRate=1000)
public void work() {
// task execution logic
}
}
the following configuration would ensure that MyTask.work()
is called once every 1000 ms:
@Configuration
@EnableScheduling
public class AppConfig {
@Bean
public MyTask task() {
return new MyTask();
}
}
Alternatively, if MyTask were annotated with @Component
, the following configuration would ensure that its @Scheduled
method is invoked at the desired interval:
@Configuration
@EnableScheduling
@ComponentScan(basePackages="com.myco.tasks")
public class AppConfig {
}
Methods annotated with @Scheduled
may even be declared directly within
@Configuration
classes:
@Configuration
@EnableScheduling
public class AppConfig {
@Scheduled(fixedRate=1000)
public void work() {
// task execution logic
}
}
By default, will be searching for an associated scheduler definition: either a unique TaskScheduler bean in the context, or a TaskScheduler bean named “taskScheduler” otherwise; the same lookup will also be performed for a ScheduledExecutorService bean. If neither of the two is resolvable, a local single-threaded default scheduler will be created and used within the registrar.
When more control is desired, a @Configuration
class may implement SchedulingConfigurer. This allows access to the underlying ScheduledTaskRegistrar instance. For example, the following example demonstrates how to customize the Executor used to execute scheduled tasks:
@Configuration
@EnableScheduling
public class AppConfig implements SchedulingConfigurer {
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.setScheduler(taskExecutor());
}
@Bean(destroyMethod="shutdown")
public Executor taskExecutor() {
return Executors.newScheduledThreadPool(100);
}
}
Note in the example above the use of @Bean(destroyMethod=“shutdown”). This ensures that the task executor is properly shut down when the Spring application context itself is closed.
Implementing SchedulingConfigurer also allows for fine-grained control over task registration via the ScheduledTaskRegistrar. For example, the following configures the execution of a particular bean method per a custom Trigger implementation:
@Configuration
@EnableScheduling
public class AppConfig implements SchedulingConfigurer {
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.setScheduler(taskScheduler());
taskRegistrar.addTriggerTask(
new Runnable() {
public void run() {
myTask().work();
}
},
new CustomTrigger()
);
}
@Bean(destroyMethod="shutdown")
public Executor taskScheduler() {
return Executors.newScheduledThreadPool(42);
}
@Bean
public MyTask myTask() {
return new MyTask();
}
}