SpringSchedule配置简单,并且由于属于Spring框架,可以通过Spring来管理bean的生命周期,从而可以降低编程的复杂度。
SpringSchedule的使用
以注解配置为例
- 配置文件中添加配置
添加相关注解配置:
xmlns:task="http://www.springframework.org/schema/task"
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.0.xsd
开启定时任务的注解扫描:
<task:annotation-driven/>
- 为方法添加@Scheduled注解
设置定时任务的方式有两种,一种是配置一定的延时时间以固定频率运行,一种是设置scon表达式
xml配置方式也很简单:
只需在配置文件中添加
<bean id="myBean" class="BeanClass">
</bean>
<task:scheduled-tasks >
<task:scheduled ref="myBean" method="myMethod" cron="0 0 1 * * ?"/>
</task:scheduled-tasks>
即可
代码分析
主要类:ScheduledAnnotationBeanPostProcessor
方法调用链:postProcessAfterInitialization()->processScheduled()->TaskSchedule.schedule()
A[postProcessAfterInitialization]-->B(postProcessAfterInitialization)
B-->C{cron?}
C-->|Y|D[ScheduledTaskRegistrar.scheduleCronTask]
C-->|N|E[ScheduledTaskRegistrar.scheduleFixedDelayTask]
ScheduledTaskRegistrar.scheduleFixedDelayTask实现比较简单,内部通过TaskScheduler的scheduleAtFixedRate方法实现任务的调度,并返回任务结果
而ScheduledTaskRegistrar.scheduleCronTask相对来说较复杂,主要看下TaskScheduler的ScheduledFuture<?> schedule(Runnable task, Trigger trigger);
方法
根据以上包的结构图来看,TaskScheduler有六个子类,
看下ThreadPoolTaskScheduler的schedule方法
@Override
public ScheduledFuture<?> schedule(Runnable task, Trigger trigger) {
ScheduledExecutorService executor = getScheduledExecutor();
try {
ErrorHandler errorHandler = this.errorHandler;
if (errorHandler == null) {
errorHandler = TaskUtils.getDefaultErrorHandler(true);
}
return new ReschedulingRunnable(task, trigger, executor, errorHandler).schedule();
}
catch (RejectedExecutionException ex) {
throw new TaskRejectedException("Executor [" + executor + "] did not accept task: " + task, ex);
}
}
其中的ReschedulingRunnable是一个Runnable的子类,同时也是ScheduledFuture的子类,它的schedule方法如下
@Nullable
public ScheduledFuture<?> schedule() {
synchronized (this.triggerContextMonitor) {
this.scheduledExecutionTime = this.trigger.nextExecutionTime(this.triggerContext);
if (this.scheduledExecutionTime == null) {
return null;
}
long initialDelay = this.scheduledExecutionTime.getTime() - System.currentTimeMillis();
this.currentFuture = this.executor.schedule(this, initialDelay, TimeUnit.MILLISECONDS);
return this;
}
}
问题来了,这块代码只看到了执行一次任务,那么后续的任务是怎么触发的?
看this.executor.schedule(this, initialDelay, TimeUnit.MILLISECONDS)
,这里的ReschedulingRunnable同时也是一个Runnable类,当执行调度时,其runnable()
方法被执行,再看其runnable()
方法
@Override
public void run() {
Date actualExecutionTime = new Date();
super.run();
Date completionTime = new Date();
synchronized (this.triggerContextMonitor) {
Assert.state(this.scheduledExecutionTime != null, "No scheduled execution");
this.triggerContext.update(this.scheduledExecutionTime, actualExecutionTime, completionTime);
if (!obtainCurrentFuture().isCancelled()) {
schedule();
}
}
}
在runnable()方法中,其schedule()
方法再次被执行,巧妙。如果不用这种方式实现,还可以怎么实现呢?