接上一篇我們來分析一下 @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線程模型探究 - 解決問題》。