文档章节

注解 @Scheduled

hyssop
 hyssop
发布于 2015/11/20 10:49
字数 1097
阅读 3298
收藏 1

我们跑定时的时候,代码的结构大致是这个样子的。

@Scheduled(cron="0 0 4 * * ?")
public void taskCycle() {
    Calendar  cal  =  Calendar.getInstance();
    cal.add(Calendar.DATE, -1);
     yesterday = new SimpleDateFormat( "yyyyMMdd").format(cal.getTime());
    //默认5000
    int height = Integer.valueOf("5000");
    //主页
    heatMapDto = createZy("zhuye",height);
    createHotMap(heatMapDto);
    //我的账户
    heatMapDto = createZy("myacount",height);
    createHotMap(heatMapDto);
    LOGGER.info("今执行了热力图定时任务" + yesterday);
}


那么,这个Scheduled(cron="0 0 4 * * ?")是如何被系统识别并且去跑定时任务呢?@Scheduled的解析器是ScheduledAnnotationBeanPostProcessor 该类实现了类BeanPostProcessor,这样在容器启动的时候会在refresh()的时候调用到所有实现了BeanPostProcessor接口的类。如下:

AbstractBeanFactory (方法:createBean(beanName, mbd, args))--->AbstractAutowireCapableBeanFactory
(方法:createBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)) ---》AbstractAutowireCapableBeanFactory  (方法:protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd))--》

protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
   Object bean = null;
   if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
      // Make sure bean class is actually resolved at this point.
      if (mbd.hasBeanClass() && !mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) 
      {
      //执行BeanPostProcessor的applyBeanPostProcessorsBeforeInstantiation接口
         bean = applyBeanPostProcessorsBeforeInstantiation(mbd.getBeanClass(), beanName);
         if (bean != null) {
         //执行BeanPostProcessor的applyBeanPostProcessorsBeforeInstantiation接口
            bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
         }
      }
      mbd.beforeInstantiationResolved = (bean != null);
   }
   return bean;
}
---》AbstractAutowireCapableBeanFactory
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
      throws BeansException {
   Object result = existingBean;
   for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
   //bean会走下每一个配置到系统中实现了接口BeanPostProcessor的类的postProcessAfterIn
   //itialization方法
      result = beanProcessor.postProcessAfterInitialization(result, beanName);
      if (result == null) {
         return result;
      }
   }
   return result;
}

到此我们知道了接口实现类的实现时机。那么@Scheduled的解析器是ScheduledAnnotationBeanPostProcessor在执行的时候又做了什么呢?让我们来看看ScheduledAnnotationBeanPostProcessor的postProcessAfterInitialization方法是实现了什么。


public Object postProcessAfterInitialization(final Object bean, String beanName) {
//获取目标类
   final Class<?> targetClass = AopUtils.getTargetClass(bean);//利用类反射为目标类方法做回调函数
   ReflectionUtils.doWithMethods(targetClass, new MethodCallback() {
      public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {//首先判断是否有@Scheduled标签
         Scheduled annotation = AnnotationUtils.getAnnotation(method, Scheduled.class);
         //如果有标签
         if (annotation != null) {
            try {//判断返回类型是否为空  (Scheduled表示不允许有返回值)
               Assert.isTrue(void.class.equals(method.getReturnType()),
                     "Only void-returning methods may be annotated with @Scheduled");
                     //Scheduled方法不允许有参数
               Assert.isTrue(method.getParameterTypes().length == 0,
                     "Only no-arg methods may be annotated with @Scheduled");
                     //判断目标类是否是jdk代理类
               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 new IllegalStateException(String.format(
                           "@Scheduled method '%s' found on bean target class '%s', " +
                           "but not found in any interface(s) for bean JDK proxy. Either " +
                           "pull the method up to an interface or switch to subclass (CGLIB) " +
                           "proxies by setting proxy-target-class/proxyTargetClass " +
                           "attribute to 'true'", method.getName(), targetClass.getSimpleName()));
                  }
               }
               //生成一个定时器任务
               Runnable runnable = new ScheduledMethodRunnable(bean, method);
               boolean processedSchedule = false;
               String errorMessage = "Exactly one of the 'cron', 'fixedDelay(String)', or 'fixedRate(String)' attributes is required";
               // Determine initial delay//首先实例化一个定时器延时时间 initialDelay =-1
               long initialDelay = annotation.initialDelay();//initialDelayString =""
               String initialDelayString = annotation.initialDelayString();
               if (!"".equals(initialDelayString)) {
                  Assert.isTrue(initialDelay < 0, "Specify 'initialDelay' or 'initialDelayString', not both");
                  if (embeddedValueResolver != null) {
                     initialDelayString = embeddedValueResolver.resolveStringValue(initialDelayString);
                  }
                  try {
                     initialDelay = Integer.parseInt(initialDelayString);
                  }
                  catch (NumberFormatException ex) {
                     throw new IllegalArgumentException(
                           "Invalid initialDelayString value \"" + initialDelayString + "\" - cannot parse into integer");
                  }
               }
      //获取cron表达式0 * * * * *
               String cron = annotation.cron();
               //检查cron表达式非空
               if (!"".equals(cron)) {
                  Assert.isTrue(initialDelay == -1, "'initialDelay' not supported for cron triggers");
                  processedSchedule = true;
                  if (embeddedValueResolver != null) {
                  //解析cron表达式
                     cron = embeddedValueResolver.resolveStringValue(cron);
                  }
                  //注册定时任务
                  registrar.addCronTask(new CronTask(runnable, cron));
               }
               // At this point we don't need to differentiate between initial delay set or not anymore
               if (initialDelay < 0) {
                  initialDelay = 0;
               }
               // Check fixed delay fixedDelay =-1
               long fixedDelay = annotation.fixedDelay();
               if (fixedDelay >= 0) {
                  Assert.isTrue(!processedSchedule, errorMessage);
                  processedSchedule = true;
                  registrar.addFixedDelayTask(new IntervalTask(runnable, fixedDelay, initialDelay));
               }
               String fixedDelayString = annotation.fixedDelayString();
               if (!"".equals(fixedDelayString)) {
                  Assert.isTrue(!processedSchedule, errorMessage);
                  processedSchedule = true;
                  if (embeddedValueResolver != null) {
                     fixedDelayString = embeddedValueResolver.resolveStringValue(fixedDelayString);
                  }
                  try {
                     fixedDelay = Integer.parseInt(fixedDelayString);
                  }
                  catch (NumberFormatException ex) {
                     throw new IllegalArgumentException(
                           "Invalid fixedDelayString value \"" + fixedDelayString + "\" - cannot parse into integer");
                  }
                  registrar.addFixedDelayTask(new IntervalTask(runnable, fixedDelay, initialDelay));
               }
               // Check fixed rate
               long fixedRate = annotation.fixedRate();
               if (fixedRate >= 0) {
                  Assert.isTrue(!processedSchedule, errorMessage);
                  processedSchedule = true;
                  registrar.addFixedRateTask(new IntervalTask(runnable, fixedRate, initialDelay));
               }
               String fixedRateString = annotation.fixedRateString();
               if (!"".equals(fixedRateString)) {
                  Assert.isTrue(!processedSchedule, errorMessage);
                  processedSchedule = true;
                  if (embeddedValueResolver != null) {
                     fixedRateString = embeddedValueResolver.resolveStringValue(fixedRateString);
                  }
                  try {
                     fixedRate = Integer.parseInt(fixedRateString);
                  }
                  catch (NumberFormatException ex) {
                     throw new IllegalArgumentException(
                           "Invalid fixedRateString value \"" + fixedRateString + "\" - cannot parse into integer");
                  }
                  registrar.addFixedRateTask(new IntervalTask(runnable, fixedRate, initialDelay));
               }
               // Check whether we had any attribute set
               Assert.isTrue(processedSchedule, errorMessage);
            }
            catch (IllegalArgumentException ex) {
               throw new IllegalStateException(
                     "Encountered invalid @Scheduled method '" + method.getName() + "': " + ex.getMessage());
            }
         }
      }
   });
   return bean;
}


到此我们把定时任务注册到了定时列表中了。那么定时列表是如何启动的呢?

ScheduledTaskRegistrar类实现了InitializingBean接口,在容器启动的时候实现了该方法

public void afterPropertiesSet() {
//定时任务启动   scheduleTasks();
}

定时任务都放到了List<ScheduledFuture>.该接口扩展了延时队列接口Delayed和异步接口Future。我们看一个ScheduledFuture的实现类。周期性的队列其实就是在再延时队列基础上的。我们知道延时队列能够实现在任务执行时间到了的时候去执行一次任务。而周期性队列的任务就是将延时队列的务执行之后把它按照周期时间继续放入到延时队列中,等待下一个执行时间到了继续该循环操作。





© 著作权归作者所有

hyssop
粉丝 20
博文 102
码字总数 111521
作品 0
昌平
程序员
私信 提问
Spring注解方式实现任务调度

原文:http://docs.spring.io/spring/docs/4.0.1.BUILD-SNAPSHOT/javadoc-api/ 注解类型:EnableScheduling @Target(value=TYPE) @Retention(value=RUNTIME)@Import(value=SchedulingConfigu......

Mr_Tank_
2014/01/04
18.6K
1
spring定时器用Annotation实现

1.ApplicationContext.xml配置 a)、需要在xmlns里面加入: xmlns:task="http://www.springframework.org/schema/task" b)、在xsi:schemaLocation中加入 http://www.springframework.org/sch......

锁力
2011/12/30
1K
1
【Spring Boot】23.定时任务

简介 项目开发中经常需要执行一些定时任务,比如需要在每天凌晨时候,分析一次前一天的日志信息。Spring为我们提供了异步执行任务调度的方式,提供TaskExecutor 、TaskScheduler 接口。 两个...

落花时节又逢卿
2018/12/28
19
0
Spring Boot:在Spring Boot中使用定时任务

本文主要介绍如何在Spring Boot中使用定时任务,假设你已经建好了一个基础的Spring Boot项目。首先,我们在项目中建立一个定时任务。 1.创建定时任务 package hello;import java.text.Simpl...

Element0506
2015/11/10
489
0
在Spring中使用注解(@Scheduled)创建计划任务

Spring3中加强了注解的使用,其中计划任务也得到了增强,现在创建一个计划任务只需要两步就完成了: 创建一个Java类,添加一个无参无返回值的方法,在方法上用@Scheduled注解修饰一下; 在S...

岸芷汀兰
2015/08/07
4.3K
0

没有更多内容

加载失败,请刷新页面

加载更多

3_数组

3_数组

行者终成事
今天
7
0
经典系统设计面试题解析:如何设计TinyURL(二)

原文链接:https://www.educative.io/courses/grokking-the-system-design-interview/m2ygV4E81AR 编者注:本文以一道经典的系统设计面试题:《如何设计TinyURL》的参考答案和解析为例,帮助...

APEMESH
今天
7
0
使用logstash同步MySQL数据到ES

概述   在生成业务常有将MySQL数据同步到ES的需求,如果需要很高的定制化,往往需要开发同步程序用于处理数据。但没有特殊业务需求,官方提供的logstash就很有优势了。   在使用logstas...

zxiaofan666
今天
10
0
X-MSG-IM-分布式信令跟踪能力

经过一周多的鏖战, X-MSG-IM的分布式信令跟踪能力已基本具备, 特点是: 实时. 只有要RX/TX就会实时产生信令跟踪事件, 先入kafka, 再入influxdb待查. 同时提供实时sub/pub接口. 完备. 可以完整...

dev5
今天
7
0
OpenJDK之CyclicBarrier

OpenJDK8,本人看的是openJDK。以前就看过,只是经常忘记,所以记录下 图1 CyclicBarrier是Doug Lea在JDK1.5中引入的,作用就不详细描述了,主要有如下俩个方法使用: await()方法,如果当前线...

克虏伯
今天
8
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部