Schedule调度能力源码剖析
一.使用
仅关注较为常见的一种使用方式,Spring框架下对能力启用的方式可以有很多中不同的形式,但是本质的原理是相同的。
1 2 3 4 |
|
二.原理解析
1.@Scheduled注解
两个作用:标注、携带调度信息
- 标注 Spring收集调度任务(bean实例及其特定方法绑定在一起被封装为可调度任务)的标识
- 调度信息 调度类型及其调度数据
调度类型
- cron表达式 unix风格的cron表达式
- 固定时延 单位(ms),仅生效一次 ??
- 固定频率 单位(ms)
2.@EnableScheduling注解
标准的spring风格的逻辑使能编码风格,封装了@Import,引入真正的生效逻辑处理类。
|
Configuration注解在Spring中的作用,引入bean实例。
|
ScheduledAnnotationBeanPostProcessor关键类,实现了MergedBeanDefinitionPostProcessor(BeanPostProcessor子类), SmartInitializingSingleton, ApplicationListener三个spring框架的hook,将类自身的处理逻辑嵌入到Spring框架的生命周期中生效。
- SmartInitializingSingleton, ApplicationListener的具体逻辑均指向了finishRegistration(),适配应用上下文存在与不存在两种场景,作用是完成ScheduledTaskRegistrar的启动。
- MergedBeanDefinitionPostProcessor.postProcessAfterInitialization()逻辑完成了自身的核心调度逻辑处理。
2.1 MergedBeanDefinitionPostProcessor.postProcessAfterInitialization()
|
trace至此,可以定位processScheduled就是核心逻辑。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 |
|
方法可以拆解为如下几步:
- 解析初始时延数据;
- 解析cron表达式,如果存在有效的cron表达式,则创建CronTask加入临时容器;
- 解析fixedDelay,如果存在有效的fixedDelay信息,则创建FixedDelayTask加入临时容器;
- 解析fixedRate,如果存在有效的fixedRate信息,则创建FixedRateTask加入临时容器;
- 将临时容器的任务加入固定容器(spring全生命周期存在)。
此处需要注意的是Schedule注解与调度任务的对应关系并非是1:1,可能是1:多,应用的过程中需要注意。
2.2 ScheduledTaskRegistrar
截止2.1已经完成了调度任务的采集、构造和存储(容器内存存储),后续的逻辑应该是任务的调度和执行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
|
ScheduledTaskRegistrar实现了InitializingBean,Spring生命周期的hook之一。
|
核心逻辑如上,其中taskScheduler的具体bean对象的创建通常是spring boot框架auto-configure注入的ThreadPoolTaskScheduler(视具体情况而定)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
|
ReschedulingRunnable作为一个重要的设计点,实现了任务的可重复调度(扩展性的支持Trigger)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
|
ScheduledExecutorService属性最终选择java.util.concurrent.ScheduledThreadPoolExecutor作为最终的执行工具类,而其schedule方法本身是一个one-shot调度方法,如何支持cron的周期性调度?关键在于command对象传递为this对象(ReschedulingRunnable),该类的run方法就是cron周期性执行的核心。