前篇 我们分析了springBoot 动态传参数的定时任务的使用,今天我们进行其源码分析:
一、启动类源码上注解 @EnableScheduling 已经分析过 源码分析
二、动态定时任务代码实现结构
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.addTriggerTask(
//1.添加任务内容(Runnable)
() -> {
System.out.println("执行动态定时任务: ";
//定时任务业务代码
},
//2.设置执行周期(Trigger)
triggerContext -> {
//2.1 从数据库获取执行周期
String cron = "";
System.out.println("cron="+cron);
//2.2 合法性校验.
if (StringUtils.isEmpty(cron)) {
// Omitted Code ..
return null;
}
//2.3 返回执行周期(Date)
return new CronTrigger(cron).nextExecutionTime(triggerContext);
}
);
}
二、分析接口 SchedulingConfigurer 及实现类
1、SchedulingConfigurer 接口
@FunctionalInterface
public interface SchedulingConfigurer {
/**
* Callback allowing a {@link org.springframework.scheduling.TaskScheduler
* TaskScheduler} and specific {@link org.springframework.scheduling.config.Task Task}
* instances to be registered against the given the {@link ScheduledTaskRegistrar}.
* @param taskRegistrar the registrar to be configured.
*/
//有个主要参数 ScheduledTaskRegistrar
void configureTasks(ScheduledTaskRegistrar taskRegistrar);
}
2、点击 ScheduledTaskRegistrar 类进入:
3、此业务场景的定时任务主要用 如下方法:
/**
* Add a Runnable task to be triggered per the given {@link Trigger}.
* @see TaskScheduler#scheduleAtFixedRate(Runnable, long)
*/
//一个线程任务参数,一个定时器参数
public void addTriggerTask(Runnable task, Trigger trigger) {
addTriggerTask(new TriggerTask(task, trigger));
}
即两个参数的实现类,这里可采用函数式编程(两个接口参数里,均有一个方法),如下代码:
Runnel接口:
@FunctionalInterface
public interface Runnable {
/**
* When an object implementing interface <code>Runnable</code> is used
* to create a thread, starting the thread causes the object's
* <code>run</code> method to be called in that separately executing
* thread.
* <p>
* The general contract of the method <code>run</code> is that it may
* take any action whatsoever.
*
* @see java.lang.Thread#run()
*/
public abstract void run();
}
Trigger 接口:
public interface Trigger {
/**
* Determine the next execution time according to the given trigger context.
* @param triggerContext context object encapsulating last execution times
* and last completion time
* @return the next execution time as defined by the trigger,
* or {@code null} if the trigger won't fire anymore
*/
@Nullable
Date nextExecutionTime(TriggerContext triggerContext);
}
对 nextExecutionTime 方法的实现就是返回一个时间,里面的核心代码如下:
//定时任务触发,可修改定时任务的执行周期
CronTrigger trigger=new CronTrigger(cron);
Date nextExecDate= trigger.nextExecutionTime(triggerContext);//点击查看源码
源码如下:
@Override
public Date nextExecutionTime(TriggerContext triggerContext) {
Date date = triggerContext.lastCompletionTime();
if (date != null) {
Date scheduled = triggerContext.lastScheduledExecutionTime();
if (scheduled != null && date.before(scheduled)) {
// Previous task apparently executed too early...
// Let's simply use the last calculated execution time then,
// in order to prevent accidental re-fires in the same second.
date = scheduled;
}
}
else {
date = new Date();
}
return this.sequenceGenerator.next(date);
}
4、延伸到此,我们查看 此处源码:
addTriggerTask(new TriggerTask(task, trigger));
TriggerTask 此处为执行业务类:点击进入
public TriggerTask(Runnable runnable, Trigger trigger) {
super(runnable);
Assert.notNull(trigger, "Trigger must not be null");
this.trigger = trigger;
}
5、进入 addTriggerTask 方法
/**
* Add a {@code TriggerTask}.
* @since 3.2
* @see TaskScheduler#scheduleAtFixedRate(Runnable, long)
*/
public void addTriggerTask(TriggerTask task) {
if (this.triggerTasks == null) {
this.triggerTasks = new ArrayList<>();
}
this.triggerTasks.add(task);//任务放入list集合中等待执行
}
6、何时执行 triggerTasks 中的任务呢?源码发现初始化时执行:
/**
* Calls {@link #scheduleTasks()} at bean construction time.
*/
@Override
public void afterPropertiesSet() {
scheduleTasks();//点击查看
}
7、进入 scheduleTasks() 方法:
/**
* Schedule all registered tasks against the underlying
* {@linkplain #setTaskScheduler(TaskScheduler) task scheduler}.
*/
@SuppressWarnings("deprecation")
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));
}
}
}
8、进入 scheduleTriggerTask(task) 方法:
/**
* Schedule the specified trigger task, either right away if possible
* or on initialization of the scheduler.
* @return a handle to the scheduled task, allowing to cancel it
* @since 4.3
*/
@Nullable
public ScheduledTask scheduleTriggerTask(TriggerTask task) {
ScheduledTask scheduledTask = this.unresolvedTasks.remove(task);
boolean newTask = false;
if (scheduledTask == null) {
scheduledTask = new ScheduledTask(task);
newTask = true;
}
if (this.taskScheduler != null) {//执行如下代码
scheduledTask.future = this.taskScheduler.schedule(task.getRunnable(), task.getTrigger());
}
else {
addTriggerTask(task);
this.unresolvedTasks.put(task, scheduledTask);
}
return (newTask ? scheduledTask : null);
}
9、ctrl+t 进入 taskScheduler.schedule 方法:
进入线程池执行此任务:
@Override
@Nullable
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);
}
}
10、进入 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;
}
}
进入:ScheduledThreadPoolExecutor 类
源码方法:
/**
* @throws RejectedExecutionException {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
*/
public ScheduledFuture<?> schedule(Runnable command,
long delay,
TimeUnit unit) {
if (command == null || unit == null)
throw new NullPointerException();
RunnableScheduledFuture<?> t = decorateTask(command,
new ScheduledFutureTask<Void>(command, null,
triggerTime(delay, unit)));
delayedExecute(t);//继续执行
return t;
}
点击 delayedExecute(t) 方法:
//此处到了底层执行代码
private void delayedExecute(RunnableScheduledFuture<?> task) {
if (isShutdown())
reject(task);
else {
super.getQueue().add(task);
if (isShutdown() &&
!canRunInCurrentRunState(task.isPeriodic()) &&
remove(task))
task.cancel(false);
else
ensurePrestart();//方法进入父类 ThreadPoolExecutor 中执行
}
}
到此 动态传参定时任务源码分析完成,大家可以多看几次,就熟悉源码了。