zoukankan      html  css  js  c++  java
  • spring源码分析之定时任务Scheduled注解

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

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

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

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Import(SchedulingConfiguration.class)
    @Documented
    public @interface EnableScheduling {
    
    }

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

    @Configuration
    public class SchedulingConfiguration {
    
        @Bean(name = AnnotationConfigUtils.SCHEDULED_ANNOTATION_PROCESSOR_BEAN_NAME)
        @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
        public ScheduledAnnotationBeanPostProcessor scheduledAnnotationProcessor() {
            return new ScheduledAnnotationBeanPostProcessor();
        }
    
    }

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

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

    protected void processScheduled(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 new IllegalStateException(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 = 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
                long 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 new IllegalArgumentException(
                                "Invalid initialDelayString value "" + initialDelayString + "" - cannot parse into integer");
                    }
                }
    
                // Check cron expression
                String 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, new CronTrigger(cron, timeZone)));
                }
    
                // At this point we don't need to differentiate between initial delay set or not anymore
                if (initialDelay < 0) {
                    initialDelay = 0;
                }
    
                // Check fixed delay
                long fixedDelay = scheduled.fixedDelay();
                if (fixedDelay >= 0) {
                    Assert.isTrue(!processedSchedule, errorMessage);
                    processedSchedule = true;
                    this.registrar.addFixedDelayTask(new IntervalTask(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 new IllegalArgumentException(
                                "Invalid fixedDelayString value "" + fixedDelayString + "" - cannot parse into integer");
                    }
                    this.registrar.addFixedDelayTask(new IntervalTask(runnable, fixedDelay, initialDelay));
                }
    
                // Check fixed rate
                long fixedRate = scheduled.fixedRate();
                if (fixedRate >= 0) {
                    Assert.isTrue(!processedSchedule, errorMessage);
                    processedSchedule = true;
                    this.registrar.addFixedRateTask(new IntervalTask(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 new IllegalArgumentException(
                                "Invalid fixedRateString value "" + fixedRateString + "" - cannot parse into integer");
                    }
                    this.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());
            }
        }

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

    cron expression
    fixedDelay
    fixedRate 

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

    /**
         * Schedule all registered tasks against the underlying {@linkplain
         * #setTaskScheduler(TaskScheduler) task scheduler}.
         */
        protected void scheduleTasks() {
            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来完成定时任务。

  • 相关阅读:
    【注册验证】 控制器(添加数据+后台验证)
    AJAX 简介
    我的博客生涯
    windows xp sp3 下载地址
    get、set、add、remove → (字段、属性)+(委托、事件)
    ArcObjects整体介绍
    委托
    对字符串的处理的又一个强大的工具,正则表达式
    对象序列化存在的原因
    关于文件操作的几个类
  • 原文地址:https://www.cnblogs.com/davidwang456/p/5680088.html
Copyright © 2011-2022 走看看