大千世界,茫茫人海,相识就是一种缘分。若此篇文章对您有帮助,点个赞或点个关注呗!
前言
上一篇博客 Springboot基于@EnableScheduling注解实现定时任务(一) 适用于简单的阻塞执行的定时任务
- 缺点
1、多个定时任务使用同一个调度线程,所以任务是阻塞执行,执行效率低;
2、如果出现任务阻塞,导致一些定时业务不能按照预定时间执行。 - 优点
1、配置简单,基于注解就可以实现定时任务;
2、使用于单个后台线程执行周期任务,并且保证按顺序执行。
SchedulingConfigurer接口实现动态定时任务
实现SchedulingConfigure接口,重写configureTasks方法,添加需执行的定时任务;接下来我会模拟实际通用业务场景实现定时任务操作,定时任务无非就是按照预设执行规则,定时执行具体的业务;
1. 创建数据库
用来存储记录需定时执行的业务方法,处理好具体的业务逻辑,定时间隔时间通过数据库获取,方便在不需要改代码的同时,实现一次部署,动态调整间隔时间;
服务启动后,先将实现类需要执行的业务方法,通过反射机制存储到数据库中,任务间隔时间可设置默认值,依据不同需求再修改;
2. 获取需定时执行的业务方法,存储到数据库中
TaskService需定时执行的业务方法接口
public interface TaskService {
/**
*同步更新需定时执行的业务方法(省略TaskInfo类)
*/
List<TaskInfo> taskList();
/**
*定时任务方法一
*/
int taskOne(String info);
/**
*定时任务方法二
*/
int taskTwo(String info);
}
TaskServiceImpl需定时执行的业务方法实现类
```java
@Service
@Slf4j
public class TaskServiceImpl implements TaskService{
@Override
public List<TaskInfo> taskList() {
List<String> task= new ArrayList<>();
//反射获取TaskServiceImpl实现类的业务方法
Method[] methods = TaskServiceImpl .class.getMethods();
//存储记录业务方法
Map<String, Method> map= new HashMap<>();
//因为通过反射获取的包含无用方法,可以利用自定义注解标识(AnnotationSign)过滤,我们需要执行的方法
for (Method method: methods) {
//判断注解标识,并且返回值为int (定时任务无非就是做一些关联第三方接口,在自己本地做一些增删改查
if (method.isAnnotationPresent(AnnotationSign.class) && m.getReturnType().equals(int.class)) {
AnnotationSign anno = methodm.getAnnotation(AnnotationSign.class);
if (anno != null) {
String name = method.getName()
//通过Mapper层存储服务的参数
task.add(tag);
map.put(name , methodm);
}
}
}
}
@Override
@AnnotationSign("定时任务方法一")
public int taskOne(String info){
log.info(Thread.currentThread().getName()+"执行定时任务方法一");
}
@Override
@AnnotationSign("定时任务方法二")
public int taskTwo(String info){
log.info(Thread.currentThread().getName()+"执行定时任务方法二");
}
}
自定义注解标识@AnnotationSign
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AnnotationSign {
String value() default "";
}
3. 实现SchedulingConfigurer接口,重写configureTasks()方法
Java线程池详解
@Slf4j
@Configuration
public class ScheduledConfigure implements SchedulingConfigurer {
@Autowired
private TaskService taskService;
@Override
public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
//创建可调度的线程池
scheduledTaskRegistrar.setScheduler(Executors.newScheduledThreadPool(10));
load(scheduledTaskRegistrar, taskService.list(), taskService);
}
//将需执行的业务方法,载入到定时任务中
public void load(ScheduledConfigure scheduled, List<TaskInfo> tasks, TaskService taskService) {
Map<String, ScheduledFuture<?>> map = scheduled.futures;
List<TaskInfo> all = tasks== null ? new ArrayList<>() : tasks;
//对比已加载的定时服务和未加载的定时服务
for (String key : map.keySet()) {
List<TaskInfo> select = all.stream().filter(item-> key.equals(item.getTask+ s.getInterval())).collect(Collectors.toList());
if (select.size() <= 0) {
scheduled.cancelTask(key);
} else {
all.remove(select.get(0));
}
}
for (TaskInfo items: all) {
Method method = reflects.get(items.getSyncTag());
if (method != null) {
//获取间隔时间,单位为分钟
int interval = sync.getInterval() * 60000;
scheduled.addTask(sync.getSyncTag() + sync.getInterval(), new FixedDelayTask(() -> {
int res = -1;
try {
res = (int) method.invoke(service, sync.getTask());
} catch (Exception ex) {
e.printStackTrace();
}
}, interval, interval));
}
}
}
}