From e048b093b56643a410ba92d7ad6584877aa61911 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 11 Jul 2023 22:11:13 +0200 Subject: [PATCH] ContextClosedEvent triggers early cancelling of scheduled tasks Closes gh-24629 See gh-27090 --- .../ScheduledAnnotationBeanPostProcessor.java | 43 ++++++++++++++----- 1 file changed, 32 insertions(+), 11 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/scheduling/annotation/ScheduledAnnotationBeanPostProcessor.java b/spring-context/src/main/java/org/springframework/scheduling/annotation/ScheduledAnnotationBeanPostProcessor.java index 6bc75b58dde7..2b164fff42ae 100644 --- a/spring-context/src/main/java/org/springframework/scheduling/annotation/ScheduledAnnotationBeanPostProcessor.java +++ b/spring-context/src/main/java/org/springframework/scheduling/annotation/ScheduledAnnotationBeanPostProcessor.java @@ -51,6 +51,8 @@ import org.springframework.context.ApplicationContextAware; import org.springframework.context.ApplicationListener; import org.springframework.context.EmbeddedValueResolverAware; +import org.springframework.context.event.ApplicationContextEvent; +import org.springframework.context.event.ContextClosedEvent; import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.core.MethodIntrospector; import org.springframework.core.Ordered; @@ -108,7 +110,7 @@ public class ScheduledAnnotationBeanPostProcessor implements ScheduledTaskHolder, MergedBeanDefinitionPostProcessor, DestructionAwareBeanPostProcessor, Ordered, EmbeddedValueResolverAware, BeanNameAware, BeanFactoryAware, ApplicationContextAware, - SmartInitializingSingleton, ApplicationListener, DisposableBean { + SmartInitializingSingleton, DisposableBean, ApplicationListener { /** * The default name of the {@link TaskScheduler} bean to pick up: {@value}. @@ -239,16 +241,6 @@ public void afterSingletonsInstantiated() { } } - @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(); - } - } - private void finishRegistration() { if (this.scheduler != null) { this.registrar.setScheduler(this.scheduler); @@ -645,4 +637,33 @@ public void destroy() { } } + + /** + * Reacts to {@link ContextRefreshedEvent} as well as {@link ContextClosedEvent}: + * performing {@link #finishRegistration()} and early cancelling of scheduled tasks, + * respectively. + */ + @Override + public void onApplicationEvent(ApplicationContextEvent event) { + if (event.getApplicationContext() == this.applicationContext) { + if (event instanceof ContextRefreshedEvent) { + // 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(); + } + else if (event instanceof ContextClosedEvent) { + synchronized (this.scheduledTasks) { + Collection> allTasks = this.scheduledTasks.values(); + for (Set tasks : allTasks) { + for (ScheduledTask task : tasks) { + // At this early point, let in-progress tasks complete still + task.cancel(false); + } + } + } + } + } + } + }