«

Spring定时任务原理

南京雨花吴彦祖 发布于 阅读:965


前言

笔者目前在一家银行工作,正在参与手机银行项目的功能开发,正好碰到一家分行搬迁,直接合并到总行营业部,因此在手机银行上涉及到网点,开户机构的功能的页面,都需要不展示该机构,当笔者刚拿到这个需求的时候,非常惊讶,认为这种功能应该早就做好了,应该是可以直接在后管中进行配置,一通分析下来,发现居然并没有这种功能。

应业务老师的要求,控制dept的这种功能应该由核心系统控制,其他系统从核心系统定时获取最新的dept,另外,业务老师决定将搬迁合并视为特殊情况,启用表中的预留的字段,定义为特殊机构,方便以后其它的特殊情况进行扩展。

因此,笔者需要开发一个定时任务,定时从核心系统获取dept信息。在开发这个功能之余,笔者对于Spring如何是实现定时任务非常好奇,于是打算阅读源码,了解其底层原理。

1. 如何开启定时任务?

要开启一个定时任务,在SpringBoot中非常方便:

启动类添加@EnableScheduling注解在自己的定时任务类中使用@Scheduled注解

@Component
public class Task1 {
    //每10秒执行一次
    @Scheduled(fixedRate = 10000)
    public void sayHello() {
        System.out.println("hello");
    }
}

2. @Scheduled注解

@EnableScheduling注解开启了定时任务的功能后,Spring就能识别到@Scheduled标注的方法,并且按照参数配置,定时执行任务,先来看看这个注解的组成。

@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Repeatable(Schedules.class)
public @interface Scheduled {

    String cron() default "";
    String zone() default "";
    long fixedDelay() default -1L;
    String fixedDelayString() default "";
    long fixedRate() default -1L;
    String fixedRateString() default "";
    long initialDelay() default -1L;
    String initialDelayString() default "";
}

@Scheduled有8个参数,先来看看这8个参数都有什么用:

cron:可以通过cron表达式的方式来配置定时任务的执行周期zone:指明cron表达式的时区fixedDelay:上一个任务调用结束后---下一次任务调用开始的间隔(要等待上次任务结束)fixedDelayString:同上,只不过给的值是String类型fixedRate:以固定间隔调用该方法(不需要等待上次任务完成)fixedRateString:同上,只不过给的值是String类型initialDelay:第一次按照fixedDelay或fixedRate执行该方法之前的等待时间initialDelayString:同上,只不过给的值是String类型
cron表达式这里不做介绍,通常可以使用一些在线的生成器来生成想要的cron表达式

3. 原理分析

其实,Spring能够实现定时任务,依赖于Spring的BeanPostProcessor接口,主要过程如下:

通过ScheduledAnnotationBeanPostProcessor类中的postProcessAfterInitialization()方法,获取所有被@Scheduled标注的方法processScheduled()中,对于一个方法上标注的多个@Scheduled注解会按照cron>fixedDelay>fixedRate的顺序放到任务队列中,并且之后会按照这个顺序执行注册定时任务,即让bean与这些定时任务形成映射关系(记录这个bean有哪些定时任务)由ScheduledTaskRegistrar通过scheduleTasks()方法来调度任务队列中的任务

public Object postProcessAfterInitialization(final Object bean, String beanName) {
        Class targetClass = AopUtils.getTargetClass(bean);
        if (!this.nonAnnotatedClasses.contains(targetClass)) {
            //获取@Scheduled标注的所有方法
            Map annotatedMethods = MethodIntrospector.selectMethods(targetClass,
                    new MethodIntrospector.MetadataLookup() {
                        @Override
                        public Set inspect(Method method) {
                            Set scheduledMethods = AnnotatedElementUtils.getMergedRepeatableAnnotations(
                                    method, Scheduled.class, Schedules.class);
                            return (!scheduledMethods.isEmpty() ? scheduledMethods : null);
                        }
                    });
            if (annotatedMethods.isEmpty()) {
                this.nonAnnotatedClasses.add(targetClass);
                if (logger.isTraceEnabled()) {
                    logger.trace("No @Scheduled annotations found on bean class: " + bean.getClass());
                }
            }
            else {
                // Non-empty set of methods
                for (Map.Entry entry : annotatedMethods.entrySet()) {
                    Method method = entry.getKey();
                    for (Scheduled scheduled : entry.getValue()) {
                        //执行这些方法
                        processScheduled(scheduled, method, bean);
                    }
                }
                if (logger.isDebugEnabled()) {
                    logger.debug(annotatedMethods.size() + " @Scheduled methods processed on bean '" + beanName +
                            "': " + annotatedMethods);
                }
            }
        }
        return bean;
    }
protected void processScheduled(Scheduled scheduled, Method method, Object bean) {
    try {
        ...
        //解析initialDelayString参数
        String initialDelayString = scheduled.initialDelayString();
        if (StringUtils.hasText(initialDelayString)) {
           ...
        }
        //解析cron参数
        String cron = scheduled.cron();
        if (StringUtils.hasText(cron)) {
            ...
            //存放到任务队列中并调度
            tasks.add(this.registrar.scheduleCronTask(new CronTask(runnable, new CronTrigger(cron, timeZone))));
        }
        ...
        //解析fixedDelay参数
        long fixedDelay = scheduled.fixedDelay();
        if (fixedDelay >= 0L) {
            Assert.isTrue(!processedSchedule, errorMessage);
            processedSchedule = true;
            tasks.add(this.registrar.scheduleFixedDelayTask(new IntervalTask(runnable, fixedDelay, initialDelay)));
        }
        String fixedDelayString = scheduled.fixedDelayString();
        if (StringUtils.hasText(fixedDelayString)) {
            ...
            //存放到任务队列中并调度
            tasks.add(this.registrar.scheduleFixedDelayTask(new IntervalTask(runnable, fixedDelay, initialDelay)));
        }
        //解析fixedRate参数
        long fixedRate = scheduled.fixedRate();
        if (fixedRate >= 0L) {
            Assert.isTrue(!processedSchedule, errorMessage);
            processedSchedule = true;
            tasks.add(this.registrar.scheduleFixedRateTask(new IntervalTask(runnable, fixedRate, initialDelay)));
        }
        String fixedRateString = scheduled.fixedRateString();
        if (StringUtils.hasText(fixedRateString)) {
            ...
            //存放到任务队列中并调度
            tasks.add(this.registrar.scheduleFixedRateTask(new IntervalTask(runnable, fixedRate, initialDelay)));
        }
        Assert.isTrue(processedSchedule, errorMessage);
        Map var19 = this.scheduledTasks;
        //,注册定时任务,将任务存放在map中,让其与bean形成映射关系
        synchronized(this.scheduledTasks) {
            Set registeredTasks = (Set)this.scheduledTasks.get(bean);
            if (registeredTasks == null) {
                registeredTasks = new LinkedHashSet(4);
                //将任务存放在map中
                this.scheduledTasks.put(bean, registeredTasks);
            }
            ((Set)registeredTasks).addAll(tasks);
        }
    } catch (IllegalArgumentException ex) {
        throw new IllegalStateException("Encountered invalid @Scheduled method '" + method.getName() + "': " + ex.getMessage());
    }
}
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));
        }
    }
}

以上就是定时任务的原理,看源码一定不能像看书一样从头看到尾,而是有针对的去阅读,当在工作中接触到新的东西的时候,在空闲时间去了解背后的底层原理,这样才能记忆的更加深刻,理解的更加透彻。

Spring定时任务原理
Spring定时任务原理
Spring定时任务原理
Spring定时任务原理