写作目的

最近看了一篇博客 “Spring Boot实现定时任务的动态增删启停” ,其实实现这个需求的前提是你要搞明白 定时任务 的实现原理,这样你才有可能实现定时任务的动态增删启停,所以下面从源码的角度跟 SpringBoot定时任务原理。

代码下载

​https://gitee.com/cbeann/Demooo/tree/master/springboot-demo/src/main/java/com/example/scheduledemo​

源码分析

引入@EnableScheduling注解做了什么?

我们打开@EnableScheduling注解源码后发现

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(SchedulingConfiguration.class)
@Documented
public @interface EnableScheduling {

}

其实改注解同Import引入了一个类SchedulingConfiguration类,该类向IOC容器中注入了一个BeanPostProcessor:ScheduledAnnotationBeanPostProcessor

@Configuration
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class SchedulingConfiguration {

@Bean(name = TaskManagementConfigUtils.SCHEDULED_ANNOTATION_PROCESSOR_BEAN_NAME)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public ScheduledAnnotationBeanPostProcessor scheduledAnnotationProcessor() {
return new ScheduledAnnotationBeanPostProcessor();
}

}

总结:@EnableScheduling 该类向IOC容器中注入了一个BeanPostProcessor:ScheduledAnnotationBeanPostProcessor

ScheduledAnnotationBeanPostProcessor初始化做了什么?

public ScheduledAnnotationBeanPostProcessor() {
this.registrar = new ScheduledTaskRegistrar();
}

总结:就如代码所示,new一个ScheduledTaskRegistrar

TaskComponent(自定义类)生命周期中ScheduledAnnotationBeanPostProcessor做了什么?

下图是ScheduledAnnotationBeanPostProcessor.postProcessAfterInitialization方法的源码,第一个方框就是把带有Scheduled注解的方法找出来,第二个方框是对每一个方法执行processScheduled逻辑。

SpringBoot定时任务源码分析_spring


下面截取ScheduledAnnotationBeanPostProcessor.processScheduled方法里的部分代码,它会把方法放到ScheduledTaskRegistrar里(ScheduledAnnotationBeanPostProcessor无参构造方法new的对象)

SpringBoot定时任务源码分析_定时任务_02

什么时候开始启动定时任务呢?

我们先详细看一下ScheduledAnnotationBeanPostProcessor的实现的接口和类,其实它实现了ApplicationListener,其实ScheduledAnnotationBeanPostProcessor也是一个监听器

public class ScheduledAnnotationBeanPostProcessor
implements ScheduledTaskHolder, MergedBeanDefinitionPostProcessor, DestructionAwareBeanPostProcessor,
Ordered, EmbeddedValueResolverAware, BeanNameAware, BeanFactoryAware, ApplicationContextAware,
SmartInitializingSingleton, ApplicationListener<ContextRefreshedEvent>,

那就有点门路了,最后肯定会调用ApplicationListener实现类(ScheduledAnnotationBeanPostProcessor)的某些方法,

SpringBoot定时任务源码分析_java_03


最后其实跟下去,你就会发现他调用了ScheduledAnnotationBeanPostProcessor里ScheduledTaskRegistrar的scheduleTasks方法

protected void scheduleTasks() {
if (this.taskScheduler == null) {
this.localExecutor = Executors.newSingleThreadScheduledExecutor();
this.taskScheduler = new ConcurrentTaskScheduler(this.localExecutor);
}
if (this.triggerTasks != null) {
for (TriggerTask task : this.triggerTasks) {
addScheduledTask(scheduleTriggerTask(task));
}
}
if (this.cronTasks != null) {
for (CronTask task : this.cronTasks) {
addScheduledTask(scheduleCronTask(task));
}
}
if (this.fixedRateTasks != null) {
for (IntervalTask task : this.fixedRateTasks) {
addScheduledTask(scheduleFixedRateTask(task));
}
}
if (this.fixedDelayTasks != null) {
for (IntervalTask task : this.fixedDelayTasks) {
addScheduledTask(scheduleFixedDelayTask(task));
}
}
}

然后就开始执行了

总结

思路

通过@EnableScheduling注解添加一个ScheduledAnnotationBeanPostProcessor,在Bean生命周期里拦截方法看是否有@Scheduled注解,如果有,把方法存到ScheduledAnnotationBeanPostProcessor里的ScheduledTaskRegistrar里。同时ScheduledAnnotationBeanPostProcessor又是linstener,最后ioc容器加载完毕后会通知linstener,然后就调用ScheduledAnnotationBeanPostProcessor里ScheduledTaskRegistrar的里存的方法去按照一定的逻辑去执行,就实现了以上逻辑。

总结

其实SpringBoot的自动装配原理也查不多,通过注解引入某个类(大多包含BeanPostProcessor),然后根据注解拦截Bean生命周期,把方法放在某个地方,然后最后通过监听器或者Runner去开启某个新功能。