聊聊springboot2的ScheduledTasksEndpoint

本文主要研究下springboot2的ScheduledTasksEndpointhtml

實例

註解形式

@Component
public class ScheduleTask {

    @Scheduled(cron = "0 0 5 * * ?")
    public void cronJob(){

    }

    @Scheduled(fixedDelay = 2*60*1000,initialDelay = 30*1000)
    public void fixedDelayJob(){

    }

    @Scheduled(fixedRate = 5 * 1000)
    public void fixedRatejob(){

    }
}

動態添加

@Configuration
public class ScheduleConfig implements SchedulingConfigurer {
    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        taskRegistrar.addCronTask(new Runnable() {
            @Override
            public void run() {
                System.out.println("hello");
            }
        },"0 0 6 * * ?");
    }
}

/actuator/scheduledtasks

{
  "cron": [
    {
      "runnable": {
        "target": "com.example.task.ScheduleTask.cronJob"
      },
      "expression": "0 0 5 * * ?"
    },
    {
      "runnable": {
        "target": "com.example.config.ScheduleConfig$1"
      },
      "expression": "0 0 6 * * ?"
    }
  ],
  "fixedDelay": [
    {
      "runnable": {
        "target": "com.example.task.ScheduleTask.fixedDelayJob"
      },
      "initialDelay": 30000,
      "interval": 120000
    }
  ],
  "fixedRate": [
    {
      "runnable": {
        "target": "com.example.task.ScheduleTask.fixedRatejob"
      },
      "initialDelay": 0,
      "interval": 5000
    }
  ]
}
這裏分了三種類型來展現,一個是cron表達式類型,一個是fixedDelay類型,一個是fixedRate類型。

源碼解析

ScheduledTasksEndpointAutoConfiguration

spring-boot-actuator-autoconfigure-2.0.0.RELEASE-sources.jar!/org/springframework/boot/actuate/autoconfigure/scheduling/ScheduledTasksEndpointAutoConfiguration.javajava

/**
 * {@link EnableAutoConfiguration Auto-configuration} for {@link ScheduledTasksEndpoint}.
 *
 * @author Andy Wilkinson
 * @since 2.0.0
 */
@Configuration
public class ScheduledTasksEndpointAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnEnabledEndpoint
    public ScheduledTasksEndpoint scheduledTasksEndpoint(
            ObjectProvider<List<ScheduledTaskHolder>> holders) {
        return new ScheduledTasksEndpoint(holders.getIfAvailable(Collections::emptyList));
    }

}
能夠看到從2.0開始有這個config,這裏主要建立了一個ScheduledTasksEndpoint,同時在構造器傳入ObjectProvider<List<ScheduledTaskHolder>> holders

ScheduledTasksEndpoint

spring-boot-actuator-2.0.1.RELEASE-sources.jar!/org/springframework/boot/actuate/scheduling/ScheduledTasksEndpoint.javaspring

/**
 * {@link Endpoint} to expose information about an application's scheduled tasks.
 *
 * @author Andy Wilkinson
 * @since 2.0.0
 */
@Endpoint(id = "scheduledtasks")
public class ScheduledTasksEndpoint {

    private final Collection<ScheduledTaskHolder> scheduledTaskHolders;

    public ScheduledTasksEndpoint(Collection<ScheduledTaskHolder> scheduledTaskHolders) {
        this.scheduledTaskHolders = scheduledTaskHolders;
    }

    @ReadOperation
    public ScheduledTasksReport scheduledTasks() {
        Map<TaskType, List<TaskDescription>> descriptionsByType = this.scheduledTaskHolders
                .stream().flatMap((holder) -> holder.getScheduledTasks().stream())
                .map(ScheduledTask::getTask).map(TaskDescription::of)
                .filter(Objects::nonNull)
                .collect(Collectors.groupingBy(TaskDescription::getType));
        return new ScheduledTasksReport(descriptionsByType);
    }

    //......
}
這裏主要根據scheduledTaskHolders建立ScheduledTasksReport

ScheduledTasksReport

spring-boot-actuator-2.0.0.RELEASE-sources.jar!/org/springframework/boot/actuate/scheduling/ScheduledTasksEndpoint.javaexpress

/**
     * A report of an application's scheduled {@link Task Tasks}, primarily intended for
     * serialization to JSON.
     */
    public static final class ScheduledTasksReport {

        private final List<TaskDescription> cron;

        private final List<TaskDescription> fixedDelay;

        private final List<TaskDescription> fixedRate;

        private ScheduledTasksReport(
                Map<TaskType, List<TaskDescription>> descriptionsByType) {
            this.cron = descriptionsByType.getOrDefault(TaskType.CRON,
                    Collections.emptyList());
            this.fixedDelay = descriptionsByType.getOrDefault(TaskType.FIXED_DELAY,
                    Collections.emptyList());
            this.fixedRate = descriptionsByType.getOrDefault(TaskType.FIXED_RATE,
                    Collections.emptyList());
        }

        public List<TaskDescription> getCron() {
            return this.cron;
        }

        public List<TaskDescription> getFixedDelay() {
            return this.fixedDelay;
        }

        public List<TaskDescription> getFixedRate() {
            return this.fixedRate;
        }

    }
這裏能夠看到report將定時任務歸爲cron、fixedDelay、fixedRate三類。

SchedulingConfiguration

spring-context-5.0.5.RELEASE-sources.jar!/org/springframework/scheduling/annotation/SchedulingConfiguration.javaspringboot

/**
 * {@code @Configuration} class that registers a {@link ScheduledAnnotationBeanPostProcessor}
 * bean capable of processing Spring's @{@link Scheduled} annotation.
 *
 * <p>This configuration class is automatically imported when using the
 * {@link EnableScheduling @EnableScheduling} annotation. See
 * {@code @EnableScheduling}'s javadoc for complete usage details.
 *
 * @author Chris Beams
 * @since 3.1
 * @see EnableScheduling
 * @see ScheduledAnnotationBeanPostProcessor
 */
@Configuration
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class SchedulingConfiguration {

    @Bean(name = TaskManagementConfigUtils.SCHEDULED_ANNOTATION_PROCESSOR_BEAN_NAME)
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public ScheduledAnnotationBeanPostProcessor scheduledAnnotationProcessor() {
        return new ScheduledAnnotationBeanPostProcessor();
    }

}
EnableScheduling會啓動這個配置,而後建立ScheduledAnnotationBeanPostProcessor

ScheduledAnnotationBeanPostProcessor

spring-context-5.0.5.RELEASE-sources.jar!/org/springframework/scheduling/annotation/ScheduledAnnotationBeanPostProcessor.javaapp

/**
 * Bean post-processor that registers methods annotated with @{@link Scheduled}
 * to be invoked by a {@link org.springframework.scheduling.TaskScheduler} according
 * to the "fixedRate", "fixedDelay", or "cron" expression provided via the annotation.
 *
 * <p>This post-processor is automatically registered by Spring's
 * {@code <task:annotation-driven>} XML element, and also by the
 * {@link EnableScheduling @EnableScheduling} annotation.
 *
 * <p>Autodetects any {@link SchedulingConfigurer} instances in the container,
 * allowing for customization of the scheduler to be used or for fine-grained
 * control over task registration (e.g. registration of {@link Trigger} tasks.
 * See the @{@link EnableScheduling} javadocs for complete usage details.
 *
 * @author Mark Fisher
 * @author Juergen Hoeller
 * @author Chris Beams
 * @author Elizabeth Chatman
 * @since 3.0
 * @see Scheduled
 * @see EnableScheduling
 * @see SchedulingConfigurer
 * @see org.springframework.scheduling.TaskScheduler
 * @see org.springframework.scheduling.config.ScheduledTaskRegistrar
 * @see AsyncAnnotationBeanPostProcessor
 */
public class ScheduledAnnotationBeanPostProcessor
        implements ScheduledTaskHolder, MergedBeanDefinitionPostProcessor, DestructionAwareBeanPostProcessor,
        Ordered, EmbeddedValueResolverAware, BeanNameAware, BeanFactoryAware, ApplicationContextAware,
        SmartInitializingSingleton, ApplicationListener<ContextRefreshedEvent>, DisposableBean {

    private final ScheduledTaskRegistrar registrar = new ScheduledTaskRegistrar();

    private final Map<Object, Set<ScheduledTask>> scheduledTasks =
            new IdentityHashMap<Object, Set<ScheduledTask>>(16);

    /**
     * Return all currently scheduled tasks, from {@link Scheduled} methods
     * as well as from programmatic {@link SchedulingConfigurer} interaction.
     * @since 5.0.2
     */
    @Override
    public Set<ScheduledTask> getScheduledTasks() {
        Set<ScheduledTask> result = new LinkedHashSet<>();
        synchronized (this.scheduledTasks) {
            Collection<Set<ScheduledTask>> allTasks = this.scheduledTasks.values();
            for (Set<ScheduledTask> tasks : allTasks) {
                result.addAll(tasks);
            }
        }
        result.addAll(this.registrar.getScheduledTasks());
        return result;
    }

    @Override
    public Object postProcessAfterInitialization(final Object bean, String beanName) {
        Class<?> targetClass = AopProxyUtils.ultimateTargetClass(bean);
        if (!this.nonAnnotatedClasses.contains(targetClass)) {
            Map<Method, Set<Scheduled>> annotatedMethods = MethodIntrospector.selectMethods(targetClass,
                    (MethodIntrospector.MetadataLookup<Set<Scheduled>>) 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
                annotatedMethods.forEach((method, scheduledMethods) ->
                        scheduledMethods.forEach(scheduled -> processScheduled(scheduled, method, bean)));
                if (logger.isDebugEnabled()) {
                    logger.debug(annotatedMethods.size() + " @Scheduled methods processed on bean '" + beanName +
                            "': " + annotatedMethods);
                }
            }
        }
        return bean;
    }

    @Override
    public void afterSingletonsInstantiated() {
        // Remove resolved singleton classes from cache
        this.nonAnnotatedClasses.clear();

        if (this.applicationContext == null) {
            // Not running in an ApplicationContext -> register tasks early...
            finishRegistration();
        }
    }

    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        if (event.getApplicationContext() == this.applicationContext) {
            // Running in an ApplicationContext -> register tasks this late...
            // giving other ContextRefreshedEvent listeners a chance to perform
            // their work at the same time (e.g. Spring Batch's job registration).
            finishRegistration();
        }
    }

    //......
}
  • getScheduledTasks實現的是ScheduledTaskHolder的接口,用於獲取註冊的定時任務,它先添加Map<Object, Set<ScheduledTask>> scheduledTasks的values,而後再合併registrar.getScheduledTasks()。這裏之因此要合併,主要是系統可能經過ScheduledTaskRegistrar在運行時動態添加/更新/覆蓋定時任務
  • 這裏postProcessAfterInitialization會去尋找@Scheduled的方法,而後調用processScheduled去註冊
  • onApplicationEvent以及afterSingletonsInstantiated會觸發finishRegistration,這個方法主要是從實現SchedulingConfigurer接口的方法裏頭獲取動態配置的定時任務信息,即將這裏new的一個ScheduledTaskRegistrar傳遞給SchedulingConfigurer實現類的接口,而後定時任務信息都統一添加到一個ScheduledTaskRegistrar中。

processScheduled

protected void processScheduled(Scheduled scheduled, Method method, Object bean) {

        //...
        tasks.add(this.registrar.scheduleCronTask(new CronTask(runnable, new CronTrigger(cron, timeZone))));
        //...
        tasks.add(this.registrar.scheduleFixedDelayTask(new FixedDelayTask(runnable, fixedDelay, initialDelay)));
        //...
        tasks.add(this.registrar.scheduleFixedRateTask(new FixedRateTask(runnable, fixedRate, initialDelay)));

        //...
            synchronized (this.scheduledTasks) {
                Set<ScheduledTask> registeredTasks = this.scheduledTasks.get(bean);
                if (registeredTasks == null) {
                    registeredTasks = new LinkedHashSet<>(4);
                    this.scheduledTasks.put(bean, registeredTasks);
                }
                registeredTasks.addAll(tasks);
            }
}
這裏將註解的定時任務經過registrar去註冊和調度,同時也將這些定時任務添加到registeredTasks中(這裏的scheduledTasks類型爲Map<Object, Set<ScheduledTask>> ,registeredTasks是這個map的value)

小結

自springboot2.0起,在actuator中新提供了ScheduledTasksEndpoint,默承認以經過/actuator/scheduledtasks來訪問當前應用的定時任務信息,很是便利。ide

doc

相關文章
相關標籤/搜索