接上一篇我们来分析一下 @Scheduled 这个定时器的注解实现
首先我看下@Scheduled注解的定义
package org.springframework.scheduling.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Repeatable(Schedules.class)
public @interface Scheduled {
String CRON_DISABLED = "-";
String cron() default "";
String zone() default "";
long fixedDelay() default -1;
String fixedDelayString() default "";
long fixedRate() default -1;
String fixedRateString() default "";
long initialDelay() default -1;
String initialDelayString() default "";
}
注解的字段分别对cron表达式、fixedDelay、fixRate进行了支持,可以传入value
紧接着我们看下@Scheduled在哪里被用到,跟进去查看一下实现
这里我们主要看ScheduledAnnotationBeanPostProcessor这个类,其他几个类我们稍后再说。
ScheduledAnnotationBeanPostProcessor是spring BeanPostProcessor的实现,postProcessAfterInitialization(Object bean, String beanName)方法在Bean初始化之后执行。
- 提取定时方法:抽取@Scheduled注解以及被注解的方法
- 处理定时方法:对定时方法进行注册委托给定时器,通过processScheduled()方法实现
processScheduled方法提取了注解上的值,cron、fixedDelay、fixRate分别包装成不同的对象,通过registrar.scheduleCronTask方法注册到registrar中
protected void processScheduled(Scheduled scheduled, Method method, Object bean) {
Runnable runnable = createRunnable(bean, method);
Set<ScheduledTask> tasks = new LinkedHashSet<>(4);
// 确定定时器初始延迟时间
long initialDelay = scheduled.initialDelay();
String initialDelayString = scheduled.initialDelayString();
...
initialDelay = parseDelayAsLong(initialDelayString);
...
// cron表达式实现
String cron = scheduled.cron();
...
tasks.add(this.registrar.scheduleCronTask(new CronTask(runnable, new CronTrigger(cron, timeZone))));
...
// fixed delay实现
long fixedDelay = scheduled.fixedDelay();
...
tasks.add(this.registrar.scheduleFixedDelayTask(new FixedDelayTask(runnable, fixedDelay, initialDelay)));
...
// fixed rate实现
long fixedRate = scheduled.fixedRate();
...
tasks.add(this.registrar.scheduleFixedRateTask(new FixedRateTask(runnable, fixedRate, initialDelay)));
...
}
}
registrar是ScheduledTaskRegistrar类的实例,ScheduledTaskRegistrar类主要结构如下
public class ScheduledTaskRegistrar implements ScheduledTaskHolder, InitializingBean, DisposableBean {
private TaskScheduler taskScheduler;
private ScheduledExecutorService localExecutor;
private List<TriggerTask> triggerTasks;
private List<CronTask> cronTasks;
private List<IntervalTask> fixedRateTasks;
private List<IntervalTask> fixedDelayTasks;
private final Map<Task, ScheduledTask> unresolvedTasks = new HashMap<>(16);
private final Set<ScheduledTask> scheduledTasks = new LinkedHashSet<>(16);
public void setTaskScheduler(TaskScheduler taskScheduler){...}
public void setScheduler(@Nullable Object scheduler) {...}
public ScheduledTask scheduleCronTask(CronTask task) {...}
public ScheduledTask scheduleFixedRateTask(FixedRateTask task) {...}
public ScheduledTask scheduleFixedDelayTask(IntervalTask task) {...}
}
以cron为例注册逻辑实现如下,把包装的Runnable方法和Trigger交由taskScheduler处理
再看下TaskScheduler这里是个接口,分别提供了三种模式的定义如下
public interface TaskScheduler {
// ****** cron支持 ******
ScheduledFuture<?> schedule(Runnable task, Trigger trigger);
ScheduledFuture<?> schedule(Runnable task, Date startTime);
// ****** FixedRate支持 ******
ScheduledFuture<?> scheduleAtFixedRate(Runnable task, Date startTime, long period);
ScheduledFuture<?> scheduleAtFixedRate(Runnable task, long period);
// ****** FixedDelay支持 ******
ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, Date startTime, long delay);
ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, long delay);
}
看到这里我们就大概明白了,上面分析可以总结为 提取定时方法委托给taskScheduler执行。这个***taskScheduler的实现才是线程模型的关键***。又上边ScheduledTaskRegistrar类我们可以看到taskScheduler是通过setTaskScheduler、setScheduler方法传入的,我们只需要找到这俩方法在上面地方被调用即可。
这俩方法都是在ScheduledAnnotationBeanPostProcessor#finishRegistration处被调用,finishRegistration在onApplicationEvent()阶段被执行
具体执行逻辑如下,注意两处判断逻辑,如果ScheduledAnnotationBeanPostProcessor实例的scheduler不为空就把它设置给ScheduledTaskRegistrar,否则进入下面加载逻。
按照先类型、再名称顺序从容器中分别查找TaskScheduler、ScheduledExecutorService对象
上述逻辑打断点发现默认通过this.registrar.setTaskScheduler(resolveSchedulerBean(this.beanFactory, TaskScheduler.class, false))方法加载到TaskScheduler实例,通过resolveNameBean得到一个NameBeanHolder实例,我们知道Spring 默认类别名为类名首字母小写,这里传入类型为TaskScheduler,holder.gerBeanName()为taskScheduler
TaskScheduler的实现有如下三种
通过debug发现加载的实例为ThreadPoolTaskScheduler类型
到这里我们已经知道了***@Scheduled是如何被装载处理的*** 、 委托执行对象(定时器执行单元)是如何加载的以及***加载的对象是ThreadPoolTaskScheduler类型***,那为什么默认是这个类型呢?别忘了我们还有一个非常重要的注解@EnableScheduling,这个注解是定时任务的开关、并且会进行缺省配置。TaskSchedulingAutoConfiguration为自动配置类
从配置文件中加载默认配置构造TaskSchedulerBuilder对象并执行构建方法builder进行构建
build()实现
ThreadPoolTaskScheduler默认线程为1,内部依赖JDK ScheduledExecutorService实现
createExecutor()实现,可以看到创建了一个ScheduledExecutorService,默认poolSize为1
由此分析与验证结果一致。@Scheduled注解默认采用单线程执行,执行单元是Spring 实现的ThreadPoolTaskScheduler封装了JDK JUC调度器ScheduledExecutorService实现。
最后再往回翻代码发现,ScheduledAnnotationBeanPostProcessor#setScheduler(Object)方法的注释已经说的很清楚,如果不进行配置,默认实现为单线程。
接下来就是解决问题了,请见下篇《Spring @Scheduled线程模型探究 - 解决问题》。