对动态修改定时任务的补充
上篇文章讲到通过定时任务动态修改定时任务的执行时间,看到有很多人喜欢,特地在此感谢,同时在这里对方案二再进行优化,增加了对时区的支持,同时去掉了自定义注解DynamicCron,默认支持所有@Scheduled的定时任务。
优化如下:
package com.lakala.zf.svas.scheduling;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.context.EmbeddedValueResolverAware;
import org.springframework.lang.Nullable;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.CronTask;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.util.StringValueResolver;
import java.lang.reflect.Method;
import java.util.*;
@Slf4j
@Component
public class SchedulingConfig implements SchedulingConfigurer, EmbeddedValueResolverAware {
@Nullable
private StringValueResolver embeddedValueResolver;
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
List<CronTask> cronTaskList = new ArrayList<>(taskRegistrar.getCronTaskList());
Iterator<CronTask> iterator = cronTaskList.iterator();
while (iterator.hasNext()) {
CronTask cronTask = iterator.next();
String fullPath = cronTask.toString();
log.info("cronTaskList:{}", fullPath);
Scheduled scheduled = getDynamicCron(fullPath);
// Check cron expression
String zone = scheduled.zone();
if (this.embeddedValueResolver != null) {
zone = this.embeddedValueResolver.resolveStringValue(zone);
}
TimeZone timeZone;
if (StringUtils.hasText(zone)) {
timeZone = StringUtils.parseTimeZoneString(zone);
} else {
timeZone = TimeZone.getDefault();
}
iterator.remove();
taskRegistrar.addTriggerTask(cronTask.getRunnable(), triggerContext -> {
CronTrigger trigger =
new CronTrigger(Objects.requireNonNull(this.embeddedValueResolver.resolveStringValue(scheduled.cron())), timeZone);
return trigger.nextExecutionTime(triggerContext);
});
log.info("addTriggerTask:{}", cronTask);
}
taskRegistrar.setCronTasksList(cronTaskList);
}
private Scheduled getDynamicCron(String fullPath) {
try {
int lastDotIndex = fullPath.lastIndexOf('.');
// 加载类
Class<?> clazz = Class.forName(fullPath.substring(0, lastDotIndex));
// 获取方法
Method method = clazz.getDeclaredMethod(fullPath.substring(lastDotIndex + 1));
// 获取注解
return method.getAnnotation(Scheduled.class);
} catch (Exception e) {
log.error("getAnnotation err", e);
throw new RuntimeException(e);
}
}
@Override
public void setEmbeddedValueResolver(@NotNull StringValueResolver resolver) {
this.embeddedValueResolver = resolver;
}
}
实现逻辑
具体看ScheduledAnnotationBeanPostProcessor,这个类是 Spring 对定时任务处理的逻辑,这也充分说明了为什么定时任务是放在 cronTasks,而不是 triggerTasks,具体逻辑请看org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor#processScheduled方法,源码很清晰,在此就不解读了,如果有朋友感兴趣,可以在评论区评论,点赞多的话我会详细解读一下,还是蛮有意思的。