1. @Scheduled 可以将一个方法标识为可定时执行的。但必须指明cron(),fixedDelay(),或者fixedRate()属性。

注解的方法必须是无输入参数并返回空类型void的。

@Scheduled注解由注册的ScheduledAnnotationBeanPostProcessor来处理,该processor可以通过手动来注册,更方面的方式是通过<task:annotation-driven/>或者@EnableScheduling来注册。@EnableScheduling可以注册的原理是什么呢?先看定义:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(SchedulingConfiguration.class)
@Documentedpublic @interfaceEnableScheduling {}

可以看到@EnableScheduling的实现由SchedulingConfiguration来完成。

@Configurationpublic classSchedulingConfiguration {@Bean(name=AnnotationConfigUtils.SCHEDULED_ANNOTATION_PROCESSOR_BEAN_NAME)@Role(BeanDefinition.ROLE_INFRASTRUCTURE)publicScheduledAnnotationBeanPostProcessor scheduledAnnotationProcessor() {return newScheduledAnnotationBeanPostProcessor();}}

从上述代码可以看出,SchedulingConfiguration注册了一个ScheduledAnnotationBeanPostProcessor。

来看一下ScheduledAnnotationBeanPostProcessor来如何处理定时任务的?

protected voidprocessScheduled(Scheduled scheduled, Method method, Object bean) {try{Assert.isTrue(void.class.equals(method.getReturnType()),"Only void-returning methods may be annotated with @Scheduled");Assert.isTrue(method.getParameterTypes().length== 0,"Only no-arg methods may be annotated with @Scheduled");if(AopUtils.isJdkDynamicProxy(bean)) {try{//Found a @Scheduled method on the target class for this JDK proxy ->//is it also present on the proxy itself?method =bean.getClass().getMethod(method.getName(), method.getParameterTypes());}catch(SecurityException ex) {ReflectionUtils.handleReflectionException(ex);}catch(NoSuchMethodException ex) {throw newIllegalStateException(String.format("@Scheduled method '%s' found on bean target class '%s' but not " +"found in any interface(s) for a dynamic proxy. Either pull the " +"method up to a declared interface or switch to subclass (CGLIB) " +"proxies by setting proxy-target-class/proxyTargetClass to 'true'",method.getName(), method.getDeclaringClass().getSimpleName()));}}else if(AopUtils.isCglibProxy(bean)) {//Common problem: private methods end up in the proxy instance, not getting delegated.if(Modifier.isPrivate(method.getModifiers())) {LogFactory.getLog(ScheduledAnnotationBeanPostProcessor.class).warn(String.format("@Scheduled method '%s' found on CGLIB proxy for target class '%s' but cannot " +"be delegated to target bean. Switch its visibility to package or protected.",method.getName(), method.getDeclaringClass().getSimpleName()));}}Runnable runnable= newScheduledMethodRunnable(bean, method);boolean processedSchedule = false;String errorMessage="Exactly one of the 'cron', 'fixedDelay(String)', or 'fixedRate(String)' attributes is required";//Determine initial delaylong initialDelay =scheduled.initialDelay();String initialDelayString=scheduled.initialDelayString();if(StringUtils.hasText(initialDelayString)) {Assert.isTrue(initialDelay< 0, "Specify 'initialDelay' or 'initialDelayString', not both");if (this.embeddedValueResolver != null) {initialDelayString= this.embeddedValueResolver.resolveStringValue(initialDelayString);}try{initialDelay=Integer.parseInt(initialDelayString);}catch(NumberFormatException ex) {throw newIllegalArgumentException("Invalid initialDelayString value \"" + initialDelayString + "\" - cannot parse into integer");}}//Check cron expressionString cron =scheduled.cron();if(StringUtils.hasText(cron)) {Assert.isTrue(initialDelay== -1, "'initialDelay' not supported for cron triggers");processedSchedule= true;String zone=scheduled.zone();if (this.embeddedValueResolver != null) {cron= this.embeddedValueResolver.resolveStringValue(cron);zone= this.embeddedValueResolver.resolveStringValue(zone);}TimeZone timeZone;if(StringUtils.hasText(zone)) {timeZone=StringUtils.parseTimeZoneString(zone);}else{timeZone=TimeZone.getDefault();}this.registrar.addCronTask(new CronTask(runnable, newCronTrigger(cron, timeZone)));}//At this point we don't need to differentiate between initial delay set or not anymoreif (initialDelay < 0) {initialDelay= 0;}//Check fixed delaylong fixedDelay =scheduled.fixedDelay();if (fixedDelay >= 0) {Assert.isTrue(!processedSchedule, errorMessage);processedSchedule= true;this.registrar.addFixedDelayTask(newIntervalTask(runnable, fixedDelay, initialDelay));}String fixedDelayString=scheduled.fixedDelayString();if(StringUtils.hasText(fixedDelayString)) {Assert.isTrue(!processedSchedule, errorMessage);processedSchedule= true;if (this.embeddedValueResolver != null) {fixedDelayString= this.embeddedValueResolver.resolveStringValue(fixedDelayString);}try{fixedDelay=Integer.parseInt(fixedDelayString);}catch(NumberFormatException ex) {throw newIllegalArgumentException("Invalid fixedDelayString value \"" + fixedDelayString + "\" - cannot parse into integer");}this.registrar.addFixedDelayTask(newIntervalTask(runnable, fixedDelay, initialDelay));}//Check fixed ratelong fixedRate =scheduled.fixedRate();if (fixedRate >= 0) {Assert.isTrue(!processedSchedule, errorMessage);processedSchedule= true;this.registrar.addFixedRateTask(newIntervalTask(runnable, fixedRate, initialDelay));}String fixedRateString=scheduled.fixedRateString();if(StringUtils.hasText(fixedRateString)) {Assert.isTrue(!processedSchedule, errorMessage);processedSchedule= true;if (this.embeddedValueResolver != null) {fixedRateString= this.embeddedValueResolver.resolveStringValue(fixedRateString);}try{fixedRate=Integer.parseInt(fixedRateString);}catch(NumberFormatException ex) {throw newIllegalArgumentException("Invalid fixedRateString value \"" + fixedRateString + "\" - cannot parse into integer");}this.registrar.addFixedRateTask(newIntervalTask(runnable, fixedRate, initialDelay));}//Check whether we had any attribute set
Assert.isTrue(processedSchedule, errorMessage);}catch(IllegalArgumentException ex) {throw newIllegalStateException("Encountered invalid @Scheduled method '" + method.getName() + "': " +ex.getMessage());}}

从上面的代码可以看出:@Scheduled有三个属性,分别是:

cron expression
fixedDelay
fixedRate 

根据这些属性的不同,都加入到ScheduledTaskRegistrar来管理定时任务:

/*** Schedule all registered tasks against the underlying {@linkplain* #setTaskScheduler(TaskScheduler) task scheduler}.*/protected voidscheduleTasks() {long now =System.currentTimeMillis();if (this.taskScheduler == null) {this.localExecutor =Executors.newSingleThreadScheduledExecutor();this.taskScheduler = new ConcurrentTaskScheduler(this.localExecutor);}if (this.triggerTasks != null) {for (TriggerTask task : this.triggerTasks) {this.scheduledFutures.add(this.taskScheduler.schedule(task.getRunnable(), task.getTrigger()));}}if (this.cronTasks != null) {for (CronTask task : this.cronTasks) {this.scheduledFutures.add(this.taskScheduler.schedule(task.getRunnable(), task.getTrigger()));}}if (this.fixedRateTasks != null) {for (IntervalTask task : this.fixedRateTasks) {if (task.getInitialDelay() > 0) {Date startTime= new Date(now +task.getInitialDelay());this.scheduledFutures.add(this.taskScheduler.scheduleAtFixedRate(task.getRunnable(), startTime, task.getInterval()));}else{this.scheduledFutures.add(this.taskScheduler.scheduleAtFixedRate(task.getRunnable(), task.getInterval()));}}}if (this.fixedDelayTasks != null) {for (IntervalTask task : this.fixedDelayTasks) {if (task.getInitialDelay() > 0) {Date startTime= new Date(now +task.getInitialDelay());this.scheduledFutures.add(this.taskScheduler.scheduleWithFixedDelay(task.getRunnable(), startTime, task.getInterval()));}else{this.scheduledFutures.add(this.taskScheduler.scheduleWithFixedDelay(task.getRunnable(), task.getInterval()));}}}}

从上面看出:

3种不同属性的task均由quartz的taskScheduler的不同方法来完成,

scheduleWithFixedDelay,
scheduleAtFixedRate,
schedule

即最终的实现由TaskScheduler来完成定时任务。

转载于:https://www.cnblogs.com/davidwang456/p/5680088.html

spring源码分析之定时任务Scheduled注解相关推荐

  1. spring源码分析之定时任务概述

    Spring框架提供了TaskExcutor的异步执行和TashScheduler的任务定时执行接口,同样spring也提供了线程池或者CommonJ的代理. TaskExecutor的类型 Simp ...

  2. spring源码分析之spring-core总结篇

    1.spring-core概览 spring-core是spring框架的基石,它为spring框架提供了基础的支持. spring-core从源码上看,分为6个package,分别是asm,cgli ...

  3. spring源码分析第六天------spring经典面试问题

    spring源码分析第六天------spring经典面试问题 1.Spring5 新特性及应用举例 2.Spring 经典的面试问题 a.什么是 Spring 框架?Spring 框架有哪些主要模块 ...

  4. spring源码分析之BeanDefinition相关

    目录 前言: BeanDefinition的家族系列 1.BeanDefintion的UML类图 2.BeanDefintion家族类详解 2.1.通用接口 2.2.BeanDefintion接口 2 ...

  5. Spring源码分析八:Mybatis ORM映射框架原理

    文章目录 (一)Mybatis单独操作数据库程序 1.1.数据库表 1.2.建立PO 1.3.建立mapper接口映射 1.4.建立Mybatis配置文件 1.5.建立mapper映射文件 1.6.测 ...

  6. Spring 源码分析 (一)——迈向 Spring 之路

    一切都是从 Bean 开始的 在 1996 年,Java 还只是一个新兴的.初出茅庐的编程语言.人们之所以关注她仅仅是因为,可以使用 Java 的 Applet 来开发 Web 应用.但这些开发者很快 ...

  7. Spring源码分析之Bean的创建过程详解

    前文传送门: Spring源码分析之预启动流程 Spring源码分析之BeanFactory体系结构 Spring源码分析之BeanFactoryPostProcessor调用过程详解 本文内容: 在 ...

  8. Spring 源码分析衍生篇十三 :事务扩展机制 TransactionSynchronization

    文章目录 一.前言 二.TransactionSynchronization 1. TransactionSynchronization 1.1 TransactionSynchronization ...

  9. Spring 源码分析衍生篇三 : lookup-method 和 replaced-method

    文章目录 一.前言 二.基本使用 1. 作用 三.原理实现 1. 预处理 1.1 AbstractBeanDefinition#prepareMethodOverrides 1.2 Autowired ...

最新文章

  1. 一位中国博士把整个 CNN 都给可视化了,可交互有细节,每次卷积 ReLU 池化都清清楚楚...
  2. 浙江大学计算机研究生分数线初试单科学科,2016年浙江大学计算机考研复试分数线_浙江大学考研分数线...
  3. R语言可视化包ggplot2绘制饼图(pie chart)实战
  4. Spring MVC中使用Swagger生成API文档和完整项目示例Demo,swagger-server-api(二十)
  5. webpack 使用教程--实时刷新测试
  6. 腾讯、百度、阿里、微软面试题精选(不断更新)
  7. E - Escape from the Island(最短路+dp)
  8. 调节e18-d80nk的测量距离_地坪研磨机磨盘平整度的调节方法及好处
  9. 飞鸽传书创造出自己的一片天吧
  10. (转)非常完善的Log4net详细说明
  11. ExtJS Panel主要配置列表
  12. bash脚本编程之十 函数
  13. 计算机病毒note01
  14. Java连接db2数据库(常用数据库连接五)
  15. Centos7系统启动盘的正确安装姿势
  16. 解决windows 2000无法安装vmtool的问题
  17. 【转】[中级]我对『PID算法』的理解 —— 原理介绍
  18. python真的好难过张嘉洵_现在好难过怎么办?
  19. jssdk信息验证失败_微信公众号开发——微信JSSDK使用
  20. 联创自助打印驱动程序 v1.0官方版

热门文章

  1. Java bitset转string_JAVA 假如String01010 我要怎样把它转成bitset?
  2. 如何用python爬虫薅羊毛_不会Python编程也能用的,薅羊毛软件使用说明
  3. linux 从grub 进入rescue,在grub的rescue模式修复linux引导
  4. android 一个字符串分两行显示_【Android】DataBindinglt;中gt;
  5. aes sample java,python-AES加密java解密
  6. linux mysql安装数据库在哪里看_linux 查看mysql安装在哪里?
  7. C语言鹦鹉学舌1,鹦鹉学舌真是简单的学舌吗?为什么他们清晰的知道在哪用哪些词...
  8. linux mysql libc.so_mysql-arm-linux-gcc编译报错:libc.soformatnotrecognized.
  9. 读计算机平面设计要什么文化好,浅谈计算机平面设计的有关论文
  10. 多线程之pthread_create()函数