序
本文主要研究下springboot2的ScheduledTasksEndpoint
实例
注解形式
@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.java
/**
* {@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.java
/**
* {@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.java
/**
* 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.java
/**
* {@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.java
/**
* 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来访问当前应用的定时任务信息,非常便利。