前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >浅谈Spring中定时任务@Scheduled源码的解析(二)

浅谈Spring中定时任务@Scheduled源码的解析(二)

原创
作者头像
半月无霜
发布2024-07-25 10:29:56
1060
发布2024-07-25 10:29:56
举报
文章被收录于专栏:半月无霜

浅谈Spring中定时任务@Scheduled源码的解析(二)

一、介绍

在上一篇文章中,我们知道了,spring是如何获取到task

那么本篇将简单解读我们是如何将这些task运行起来的

二、如何运行

上面的代码只是讲述了如何获取到task,那么接下来如何将这些task当成定时任务来执行呢

我们接着往下看,还是当前这个类,实现了ApplicationListener<ContextRefreshedEvent>

这也就代表着在容器启动完成后,会调用这个方法void onApplicationEvent(E event);

代码语言:javascript
复制
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
    if (event.getApplicationContext() == this.applicationContext) {
        // Running in an ApplicationContext -> register tasks this late...
        // giving other ContextRefreshedEvent listeners a chance to perform
        // their work at the same time (e.g. Spring Batch's job registration).
        finishRegistration();
    }
}

里面调用了这个方法finishRegistration();那就继续看

代码语言:javascript
复制
private void finishRegistration() {
    if (this.scheduler != null) {
        this.registrar.setScheduler(this.scheduler);
    }
​
    if (this.beanFactory instanceof ListableBeanFactory) {
        Map<String, SchedulingConfigurer> beans =
            ((ListableBeanFactory) this.beanFactory).getBeansOfType(SchedulingConfigurer.class);
        List<SchedulingConfigurer> configurers = new ArrayList<>(beans.values());
        AnnotationAwareOrderComparator.sort(configurers);
        for (SchedulingConfigurer configurer : configurers) {
            configurer.configureTasks(this.registrar);
        }
    }
​
    if (this.registrar.hasTasks() && this.registrar.getScheduler() == null) {
        Assert.state(this.beanFactory != null, "BeanFactory must be set to find scheduler by type");
        try {
            // Search for TaskScheduler bean...
            this.registrar.setTaskScheduler(resolveSchedulerBean(this.beanFactory, TaskScheduler.class, false));
        }
        catch (NoUniqueBeanDefinitionException ex) {
            if (logger.isTraceEnabled()) {
                logger.trace("Could not find unique TaskScheduler bean - attempting to resolve by name: " +
                             ex.getMessage());
            }
            try {
                this.registrar.setTaskScheduler(resolveSchedulerBean(this.beanFactory, TaskScheduler.class, true));
            }
            catch (NoSuchBeanDefinitionException ex2) {
                if (logger.isInfoEnabled()) {
                    logger.info("More than one TaskScheduler bean exists within the context, and " +
                                "none is named 'taskScheduler'. Mark one of them as primary or name it 'taskScheduler' " +
                                "(possibly as an alias); or implement the SchedulingConfigurer interface and call " +
                                "ScheduledTaskRegistrar#setScheduler explicitly within the configureTasks() callback: " +
                                ex.getBeanNamesFound());
                }
            }
        }
        catch (NoSuchBeanDefinitionException ex) {
            if (logger.isTraceEnabled()) {
                logger.trace("Could not find default TaskScheduler bean - attempting to find ScheduledExecutorService: " +
                             ex.getMessage());
            }
            // Search for ScheduledExecutorService bean next...
            try {
                this.registrar.setScheduler(resolveSchedulerBean(this.beanFactory, ScheduledExecutorService.class, false));
            }
            catch (NoUniqueBeanDefinitionException ex2) {
                if (logger.isTraceEnabled()) {
                    logger.trace("Could not find unique ScheduledExecutorService bean - attempting to resolve by name: " +
                                 ex2.getMessage());
                }
                try {
                    this.registrar.setScheduler(resolveSchedulerBean(this.beanFactory, ScheduledExecutorService.class, true));
                }
                catch (NoSuchBeanDefinitionException ex3) {
                    if (logger.isInfoEnabled()) {
                        logger.info("More than one ScheduledExecutorService bean exists within the context, and " +
                                    "none is named 'taskScheduler'. Mark one of them as primary or name it 'taskScheduler' " +
                                    "(possibly as an alias); or implement the SchedulingConfigurer interface and call " +
                                    "ScheduledTaskRegistrar#setScheduler explicitly within the configureTasks() callback: " +
                                    ex2.getBeanNamesFound());
                    }
                }
            }
            catch (NoSuchBeanDefinitionException ex2) {
                if (logger.isTraceEnabled()) {
                    logger.trace("Could not find default ScheduledExecutorService bean - falling back to default: " +
                                 ex2.getMessage());
                }
                // Giving up -> falling back to default scheduler within the registrar...
                logger.info("No TaskScheduler/ScheduledExecutorService bean found for scheduled processing");
            }
        }
    }
​
    this.registrar.afterPropertiesSet();
}
  1. 首先判断scheduler存不存在,这个是肯定不在的,我们跳过这个判断
  2. 再然后注意,它获取了什么,SchedulingConfigurer.java这个bean有印象不。我们在前面设置自己的线程池时,实现了这个类。
    1. 所以这里获取到我们的bean,并执行configurer.configureTasks(this.registrar);
    2. 将我们的线程池,设置到registrar注册器中
  3. 再后来判断如果有任务,且没有调度器的话
    1. 尝试在beanFactory中查找TaskScheduler类型的bean
      1. 若找到多个,尝试通过名称'taskScheduler'解决
      2. 若找不到,尝试查找ScheduledExecutorService类型的bean,并重复上述逻辑
    2. 若真的找不到,会使用registrar的默认调度器
  4. 最后调用注册器this.registrar.afterPropertiesSet();
代码语言:javascript
复制
@Override
public void afterPropertiesSet() {
    scheduleTasks();
}
代码语言:javascript
复制
@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));
        }
    }
}

来到scheduleTasks(),这个方法就是将任务加入到调度器了

可以看到上面,如果没有调度器的话 它是自己生成一个单线程的线程池,作为调度器 this.localExecutor = Executors.newSingleThreadScheduledExecutor();

triggerTaskscronTasksfixedRateTasksfixedDelayTasks

这四个集合,若是里面有任务,将循环着将任务添加到调度器中,我们以这个方法为例

addScheduledTask(scheduleCronTask(task));

代码语言:javascript
复制
@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);
}
  1. 首先从unresolvedTasks中移除任务,给予变量scheduledTask
  2. 判断这个任务存不存在
    1. 如果为空,则新建一个
  3. 判断任务调度器,如果存在
    1. 用调度器调用方法,传入任务的runnable、以及任务的trigger
  4. 判断任务调度器,如果不存在
    1. 那么任务将被标记为待处理,存储在unresolvedTasks
    2. 等到时候有了调度器,就能运行了

四、最后

那么,spring的定时任务源码,就先解读到这边了

注意,上面讲到了spring的定时任务默认的线程池是单线程的

到时候面试的时候,不要忘记了

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 浅谈Spring中定时任务@Scheduled源码的解析(二)
    • 一、介绍
      • 二、如何运行
        • 四、最后
        相关产品与服务
        容器服务
        腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档