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 e91657bd320..d49e4c69dfb 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 @@ -310,8 +310,9 @@ public class ScheduledAnnotationBeanPostProcessor logger.trace(annotatedMethods.size() + " @Scheduled methods processed on bean '" + beanName + "': " + annotatedMethods); } - if ((this.beanFactory != null && !this.beanFactory.isSingleton(beanName)) || - (this.beanFactory instanceof SingletonBeanRegistry sbr && sbr.containsSingleton(beanName))) { + if ((this.beanFactory != null && + (!this.beanFactory.containsBean(beanName) || !this.beanFactory.isSingleton(beanName)) || + (this.beanFactory instanceof SingletonBeanRegistry sbr && sbr.containsSingleton(beanName)))) { // Either a prototype/scoped bean or a FactoryBean with a pre-existing managed singleton // -> trigger manual cancellation when ContextClosedEvent comes in this.manualCancellationOnContextClose.add(bean); diff --git a/spring-context/src/test/java/org/springframework/scheduling/annotation/ScheduledAnnotationBeanPostProcessorTests.java b/spring-context/src/test/java/org/springframework/scheduling/annotation/ScheduledAnnotationBeanPostProcessorTests.java index 37213e2af3b..4c430560050 100644 --- a/spring-context/src/test/java/org/springframework/scheduling/annotation/ScheduledAnnotationBeanPostProcessorTests.java +++ b/spring-context/src/test/java/org/springframework/scheduling/annotation/ScheduledAnnotationBeanPostProcessorTests.java @@ -29,6 +29,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; +import java.util.Set; import java.util.concurrent.TimeUnit; import org.assertj.core.api.AbstractAssert; @@ -270,6 +271,24 @@ class ScheduledAnnotationBeanPostProcessorTests { assertThat(task.getInitialDelayDuration()).isEqualTo(Duration.ofMillis(2_000L)); } + @Test + void oneTimeTaskOnNonRegisteredBean() { + BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class); + context.registerBeanDefinition("postProcessor", processorDefinition); + context.refresh(); + + ScheduledTaskHolder postProcessor = context.getBean("postProcessor", ScheduledTaskHolder.class); + assertThat(postProcessor.getScheduledTasks()).hasSize(0); + + Object target = context.getAutowireCapableBeanFactory().createBean(OneTimeTaskBean.class); + assertThat(postProcessor.getScheduledTasks()).hasSize(1); + @SuppressWarnings("unchecked") + Set manualTasks = (Set) + new DirectFieldAccessor(postProcessor).getPropertyValue("manualCancellationOnContextClose"); + assertThat(manualTasks).hasSize(1); + assertThat(manualTasks).contains(target); + } + @Test void cronTask() { BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class);