您的位置:首页 > 编程语言 > Java开发

spring boot 学习笔记(4) 动态创建定时任务

2017-03-21 00:00 483 查看
摘要: 在spring boot中使用定时任务特别简单,只需要使用@EnableScheduling注解开启定时任务,然后在每个定时任务类方法上加上@Scheduled注解,就可以轻松的实现定时任务。

最近项目里面需要做动态定时任务的功能,需要查询数据库里面的某个表,然后动态的创建、修改、删除定时任务。作为一个伸手党,本来想着上网一查就能找到现成的方案,结果找了半天也没找到合适的,没办法自己动手研究一下吧。
还好在官方文档中翻到了一个SchedulingConfigurer类,查看源码发现configureTasks方法中,可以通过ScheduledTaskRegistrar.addCronTask实例来添加任务,嗯,看起来有戏,动手试一下。实现思路如下:

通过@Scheduled注解创建一个定时任务,30秒执行一次,用于加载数据库中的定时任务列表

继承SchedulingConfigurer类,通过ScheduledTaskRegistrar.addCronTask实例来添加任务

创建DynamicTaskRunable 用来执行对应的任务操作

package com.spbd.task.job;

import java.util.List;

import javax.annotation.PreDestroy;

import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.CronTask;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.stereotype.Component;

import com.spbd.task.model.TimingTask;
@Component
public class DynamicTaskConfigurer implements SchedulingConfigurer {

private volatile ScheduledTaskRegistrar registrar;

@Override
public void configureTasks(ScheduledTaskRegistrar registrar) {
this.registrar = registrar;
}

public void refreshTasks(List<TimingTask> tasks){
for (TimingTask tt : tasks) {
//判断是否已经存在该任务
if(hasTask(tt.getTaskId())){
DynamicTaskRunable t = new DynamicTaskRunable(tt.getTaskId());
String expression = tt.getExpression();
CronTask task = new CronTask(t, expression);
registrar.addCronTask(task);
}
}
}

private boolean hasTask(Integer taskId){
List<CronTask> taskList = registrar.getCronTaskList();
for (CronTask cronTask : taskList) {
if(cronTask.getRunnable() instanceof DynamicTaskRunable){
DynamicTaskRunable r = (DynamicTaskRunable) cronTask.getRunnable();
if(r.getTaskId().equals(taskId)){
return true;
}
}
}
return false;
}

@PreDestroy
public void destroy() {
this.registrar.destroy();
}
}

package com.spbd.task.job;

import java.util.ArrayList;
import java.util.List;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import com.spbd.task.model.TimingTask;

@Component
public class DynamicTask {

private final static Logger LOGGER =Logger.getLogger(DynamicTaskRunable.class);
@Autowired
private DynamicTaskConfigurer dynamicTaskConfigurer;

@Scheduled(cron="0/30 * * * * ?")
public void loadTasks(){
LOGGER.info("DynamicTask 正在执行......");
//从数据库查询
List<TimingTask> tasks = new ArrayList<TimingTask>();
dynamicTaskConfigurer.refreshTasks(tasks);
}
}

package com.spbd.task.job;

import org.apache.log4j.Logger;

public class DynamicTaskRunable implements Runnable{

private final static Logger LOGGER =Logger.getLogger(DynamicTaskRunable.class);

private Integer taskId;

public DynamicTaskRunable(Integer taskId) {
this.taskId = taskId;
}
@Override
public void run() {
LOGGER.info("DynamicTaskRunable is running, taskId:"+taskId);
}

public Integer getT
7ff0
askId() {
return taskId;
}
}

当我几下把代码写出来之后,迫不及待的运行起来试了试,然而并没有什么用。。。

又看了一下源码才发现ScheduledTaskRegistrar.addCronTask只是把任务放到列表里面,只有在调用scheduleTasks方法的时候才会真正的创建定时任务,而这个方法只有在实例化之后才会去调用。于是脑袋上的小灯泡闪了一下,在refreshTasks方法中调用ScheduledTaskRegistrar.afterPropertiesSet()。于是我又迫不及待的试了试,诶,居然可以了。

毕竟我还是太年轻,正当我沾沾自喜的时候,发现定时任务的数量越来越多了,我勒个去,这不是我想要的。看样子一定是我打开的方式不对。继续看源码吧!

仔细查看源码发现ScheduledTaskRegistrar.scheduleTasks()方法每次都会遍历任务列表并且调用TaskScheduler.schedule()创建定时任务,所以才会创建很多的定时任务。

既然是通过TaskScheduler.schedule()创建定时任务的,那就在它上面想办法吧。改造之后的DynamicTaskConfigurer类如下:

package com.spbd.task.job;

import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledFuture;

import javax.annotation.PreDestroy;

import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.CronTask;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import com.spbd.task.model.TimingTask;
@Component
public class DynamicTaskConfigurer implements SchedulingConfigurer {

private volatile ScheduledTaskRegistrar registrar;

private final ConcurrentHashMap<Integer, ScheduledFuture<?>> scheduledFutures = new ConcurrentHashMap<Integer, ScheduledFuture<?>>();
private final ConcurrentHashMap<Integer, CronTask> cronTasks = new ConcurrentHashMap<Integer, CronTask>();

@Override
public void configureTasks(ScheduledTaskRegistrar registrar) {
this.registrar = registrar;
}

public void refreshTasks(List<TimingTask> tasks){
//取消已经删除的策略任务
Set<Integer> sids = scheduledFutures.keySet();
for (Integer sid : sids) {
if(!exists(tasks, sid)){
scheduledFutures.get(sid).cancel(false);
}
}
for (TimingTask tt : tasks) {
DynamicTaskRunable t = new DynamicTaskRunable(tt.getTaskId());
String expression = tt.getExpression();
if(StringUtils.isEmpty(expression)){
continue;
}
if(scheduledFutures.containsKey(tt.getTaskId()) && cronTasks.get(tt.getTaskId()).getExpression().equals(expression)){
continue;
}
//如果策略执行时间发生了变化,则取消当前策略的任务
if(scheduledFutures.containsKey(tt.getTaskId())){
scheduledFutures.get(tt.getTaskId()).cancel(false);
scheduledFutures.remove(tt.getTaskId());
cronTasks.remove(tt.getTaskId());
}
CronTask task = new CronTask(t, expression);
ScheduledFuture<?> future = registrar.getScheduler().schedule(task.getRunnable(), task.getTrigger());
cronTasks.put(tt.getTaskId(), task);
scheduledFutures.put(tt.getTaskId(), future);
}
}

private boolean exists(List<TimingTask> tasks,Integer tid){
for(TimingTask task:tasks){
if(task.getTaskId().equals(tid)){
return true;
}
}
return false;
}

@PreDestroy
public void destroy() {
this.registrar.destroy();
}
}

再跑起来看了一下,暂时没有问题,后面再充分测试看看。。。

https://github.com/sergewu/spbd/tree/master/spbd-task

THE END
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息