平时我们使用@Scheduled注解,每次都需要去修改代码。
现在有需求,需要通过在数据库配置cron表达式,以及控制是否开启关闭、
gitee地址:https://gitee.com/nothix/springboot-dynamic-task.git
github地址: https://github.com/shan165310175/springboot-dynamic-task
create table dynamic_task_config
(
id int auto_increment
primary key,
name varchar(255) null comment '名称',
remark varchar(255) null comment '描述',
bean_name varchar(255) not null comment 'bean名称',
cron varchar(56) not null,
status int default 0 null comment '0 ok 1 禁用',
create_time datetime null,
update_time datetime null,
constraint dynamic_task_config_beanName_uindex
unique (bean_name)
);
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-test
test
org.springframework.boot
spring-boot-starter-actuator
org.springframework.boot
spring-boot-starter-cache
org.springframework.boot
spring-boot-starter-data-redis
org.springframework.boot
spring-boot-configuration-processor
org.apache.commons
commons-pool2
commons-io
commons-io
2.6
mysql
mysql-connector-java
org.apache.httpcomponents
httpclient
io.springfox
springfox-swagger2
2.9.2
io.springfox
springfox-swagger-ui
2.9.2
com.thetransactioncompany
cors-filter
1.3.2
com.alibaba
druid-spring-boot-starter
1.1.18
com.baomidou
mybatis-plus-boot-starter
3.0.7.1
org.projectlombok
lombok
commons-lang
commons-lang
2.6
org.springframework.boot
spring-boot-dependencies
2.0.4.RELEASE
pom
import
service-task
org.apache.maven.plugins
maven-compiler-plugin
1.8
org.springframework.boot
spring-boot-maven-plugin
repackage
DyTask接口,所有定时任务类,实现该接口
/**
* 动态任务
*/
public interface DyTask {
void run();
}
@RegisterTask是自定义类,通过该注解,启动时候可以自动把配置信息存入数据库。
@Component
@Slf4j
public class TestDyTask implements DyTask {
/**
* 启动后自动将信息注入到数据库
*/
@RegisterTask(enable = true, override = false, name = "动态任务1 - 测试", remark = "这是备注", cron = "0 */5 * * * ?", status = 0)
@Override
public void run() {
log.info("TestDyTask 测试任务。。。" + LocalDateTime.now());
}
}
public interface DyTaskManager {
/**
* 直接执行定时任务
*/
boolean runTask(String beanName);
/**
* 开始定时任务
*
* @param beanName
* @param cron
*
* @return
*/
boolean scheduleTask(String beanName, String cron);
/**
* 取消定时任务
*
* @param beanName
*
* @return
*/
boolean cancelTask(String beanName);
}
@Service
@Slf4j
public class DefaultDyTaskManager implements DyTaskManager, CommandLineRunner, ApplicationContextAware {
private ApplicationContext context;
Map taskMap = new ConcurrentHashMap<>();
@Autowired
private ThreadPoolTaskScheduler taskScheduler;
@Autowired
private IDynamicTaskConfigService dynamicTaskConfigService;
@Override
public boolean cancelTask(String beanName) {
if (StringUtils.isBlank(beanName)) {
return false;
}
DyTaskInfo task = taskMap.remove(beanName);
if (task != null && task.getFuture() != null) {
task.getFuture().cancel(true);
return true;
}
return false;
}
@Override
public boolean runTask(String beanName) {
DyTask target = null;
try {
target = (DyTask) context.getBean(beanName);
}
catch (Throwable e) {
throw new ServiceException("无法获取bean信息, 无法执行任务:" + e.getMessage());
}
// 执行
target.run();
return true;
}
@Override
public boolean scheduleTask(String beanName, String cron) {
DyTask target = null;
try {
target = (DyTask) context.getBean(beanName);
}
catch (Throwable e) {
throw new ServiceException("无法获取bean信息, 无法执行任务:" + e.getMessage());
}
Method method = null;
try {
method = target.getClass().getMethod("run");
}
catch (NoSuchMethodException e) {
throw new ServiceException("无法获取run方法, 无法执行任务:" + e.getMessage());
}
try {
CronTask cronTask = new CronTask(createRunnable(target, method),
new CronTrigger(cron, TimeZone.getTimeZone(ZoneId.systemDefault())));
DyTaskInfo taskInfo = new DyTaskInfo(cronTask);
ScheduledFuture> future = taskScheduler.schedule(cronTask.getRunnable(), cronTask.getTrigger());
taskInfo.setFuture(future);
cancelTask(beanName);
taskMap.put(beanName, taskInfo);
return true;
}
catch (Throwable e) {
log.info("配置异常:{}", e.getMessage());
}
return true;
}
private Runnable createRunnable(Object target, Method method) {
Method invocableMethod = AopUtils.selectInvocableMethod(method, target.getClass());
return new ScheduledMethodRunnable(target, invocableMethod);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
context = applicationContext;
}
@Override
public void run(String... args) throws Exception {
List taskConfigs = dynamicTaskConfigService.findAllByStatus(0);
taskConfigs.forEach(config -> {
if (StringUtils.isNotBlank(config.getBeanName()) && StringUtils.isNotBlank(config.getCron())) {
log.info(">>> 初始化定时任务: {},{},{}", config.getName(), config.getBeanName(), config.getCron());
try {
scheduleTask(config.getBeanName(), config.getCron());
}
catch (Throwable e) {
log.error("初始化失败: {}", e.getMessage());
}
}
});
}
}