前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Spring定时任务原理

Spring定时任务原理

原创
作者头像
小刘Learning
发布2024-03-17 17:44:02
1990
发布2024-03-17 17:44:02
举报
文章被收录于专栏:源码阅读源码阅读

前言

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

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

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

1. 如何开启定时任务?

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

  1. 启动类添加@EnableScheduling注解
  2. 在自己的定时任务类中使用@Scheduled注解
代码语言:java
复制
@Component
public class Task1 {
    //每10秒执行一次
    @Scheduled(fixedRate = 10000)
    public void sayHello() {
        System.out.println("hello");
    }
}

2. @Scheduled注解

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

代码语言:java
复制
@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个参数都有什么用:

  1. cron:可以通过cron表达式的方式来配置定时任务的执行周期
  2. zone:指明cron表达式的时区
  3. fixedDelay:上一个任务调用结束后---下一次任务调用开始的间隔(要等待上次任务结束)
  4. fixedDelayString:同上,只不过给的值是String类型
  5. fixedRate:以固定间隔调用该方法(不需要等待上次任务完成)
  6. fixedRateString:同上,只不过给的值是String类型
  7. initialDelay:第一次按照fixedDelay或fixedRate执行该方法之前的等待时间
  8. initialDelayString:同上,只不过给的值是String类型

cron表达式这里不做介绍,通常可以使用一些在线的生成器来生成想要的cron表达式

3. 原理分析

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

  1. 通过ScheduledAnnotationBeanPostProcessor类中的postProcessAfterInitialization()方法,获取所有被@Scheduled标注的方法
  2. processScheduled()中,对于一个方法上标注的多个@Scheduled注解会按照cron>fixedDelay>fixedRate的顺序放到任务队列中,并且之后会按照这个顺序执行
  3. 注册定时任务,即让bean与这些定时任务形成映射关系(记录这个bean有哪些定时任务)
  4. ScheduledTaskRegistrar通过scheduleTasks()方法来调度任务队列中的任务
代码语言:java
复制
public Object postProcessAfterInitialization(final Object bean, String beanName) {
		Class<?> targetClass = AopUtils.getTargetClass(bean);
		if (!this.nonAnnotatedClasses.contains(targetClass)) {
            //获取@Scheduled标注的所有方法
			Map<Method, Set<Scheduled>> annotatedMethods = MethodIntrospector.selectMethods(targetClass,
					new MethodIntrospector.MetadataLookup<Set<Scheduled>>() {
						@Override
						public Set<Scheduled> inspect(Method method) {
							Set<Scheduled> 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<Method, Set<Scheduled>> 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;
	}
代码语言:java
复制
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<ScheduledTask> 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());
    }
}
代码语言:java
复制
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));
        }
    }
}

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

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

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

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 1. 如何开启定时任务?
  • 2. @Scheduled注解
  • 3. 原理分析
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档