spring定时任务-源码解析
spring定时任务-几种使用
前言
利用spring定时任务源码解析出,自己定义注解,达到同一实践多任务的执行
自定义注解
自己做一个自己需要的注解 ,并且可以采用源码中的方式进行管理
- 将ScheduledAnnotationBeanPostProcessor 类复制 重改名字
- 定义ScheduledAnnotation 注解
- postProcessAfterInitialization 中的代码修改
- 按照自己代码需要修改
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
if (bean instanceof AopInfrastructureBean || bean instanceof TaskScheduler
|| bean instanceof ScheduledExecutorService) {
// Ignore AOP infrastructure such as scoped proxies.
return bean;
}
Class<?> targetClass = AopProxyUtils.ultimateTargetClass(bean);
if (!this.nonAnnotatedClasses.contains(targetClass)) {
Map<Method, Set<ScheduledAnnotation>> annotatedMethods = MethodIntrospector.selectMethods(targetClass,
(MethodIntrospector.MetadataLookup<Set<ScheduledAnnotation>>) method -> {
Set<ScheduledAnnotation> scheduledMethods = AnnotatedElementUtils
.getMergedRepeatableAnnotations(method, ScheduledAnnotation.class,
ScheduledAnnotations.class);
return (!scheduledMethods.isEmpty() ? scheduledMethods : null);
});
if (annotatedMethods.isEmpty()) {
this.nonAnnotatedClasses.add(targetClass);
if (logger.isTraceEnabled()) {
logger.trace("No @Scheduled annotations found on bean class: " + targetClass);
}
} else {
// Non-empty set of methods
annotatedMethods.forEach((method, scheduledMethods) -> scheduledMethods
.forEach(scheduled -> processScheduled(scheduled, method, bean)));
if (logger.isTraceEnabled()) {
logger.trace(annotatedMethods.size() + " @Scheduled methods processed on bean '" + beanName + "': "
+ annotatedMethods);
}
}
}
return bean;
}
自定义规则
- 解析完任务 并自定义好规则进行 将任务放到注册器中;
- 上篇文章中动态添加任务到注册器中,相对于隔绝了需要管理任务注册器,并且任务注册器会自动删除任务,也不需要自己清理任务,当然自己清理那最好,因为spring的注解解析器,就是自己也会管理这些任务,并在销毁时清理。
/**
* 解析任务并对添加入注册器中
*
* @param scheduled
* @param method
* @param taskList
* @param bean
*/
protected void processScheduled(ScheduledAnnotation scheduled, Method method, Object bean) {
try {
Runnable runnable = createRunnable(bean, method);
boolean processedSchedule = false;
String errorMessage = "Exactly one of the 'cron', 'fixedDelay(String)', or 'fixedRate(String)' attributes is required";
Set<ScheduledTask> tasks = new LinkedHashSet<>(4);
// Determine initial delay
long initialDelay = scheduled.initialDelay();
String initialDelayString = scheduled.initialDelayString();
if (StringUtils.hasText(initialDelayString)) {
Assert.isTrue(initialDelay < 0, "Specify 'initialDelay' or 'initialDelayString', not both");
if (this.embeddedValueResolver != null) {
initialDelayString = this.embeddedValueResolver.resolveStringValue(initialDelayString);
}
if (StringUtils.hasLength(initialDelayString)) {
try {
initialDelay = parseDelayAsLong(initialDelayString);
} catch (RuntimeException ex) {
throw new IllegalArgumentException("Invalid initialDelayString value \"" + initialDelayString
+ "\" - cannot parse into long");
}
}
}
自定义注解一定要屏蔽下面代码
- 你在使用EnableScheduling 开启spring的注解解释器时,会执行两个任务
实践问题
- 多个任务同一时间执行问题
由于初始化任务 java.util.concurrent .ScheduledThreadPoolExecutor 默认为单线程的线程池 - 可以采用 在创建的时候自定义核心线程数的大小
自定义注解器中ScheduledTaskParser设置
- 在实现SchedulingConfigurer 接口里面会获得任务注册器 也可以修改到核心线程数的大小
- Spring是通过动态创建调度器 所以也可以注册bean的方式
一个任务执行时间如果超过了间隔时间
- cron表达式 源码定点去执行
- cron都是按照整点来运行的,比如5秒一次,他会在0,5,10…秒运行,如果任务时间点还没运行结束,那么就会跳过这次任务。
动态添加任务到注册器中 做定时任务
- Spring自定义ScheduledTaskParser(注解任务解析器)执行任务之前,会调用实现SchedulingConfigurer接口的configureTasks方法,
- 我们可以自定义实现SchedulingConfigurer
接口就能做添加任务进去,并且这个bean要交给spring进行管理,并且也可以设置其核心线程数