Skip to content

Commit 9d9e621

Browse files
committed
Defensive singleton check for non-registered bean
Closes gh-33286
1 parent 5aa3883 commit 9d9e621

File tree

2 files changed

+22
-2
lines changed

2 files changed

+22
-2
lines changed

spring-context/src/main/java/org/springframework/scheduling/annotation/ScheduledAnnotationBeanPostProcessor.java

+3-2
Original file line numberDiff line numberDiff line change
@@ -308,8 +308,9 @@ public Object postProcessAfterInitialization(Object bean, String beanName) {
308308
logger.trace(annotatedMethods.size() + " @Scheduled methods processed on bean '" + beanName +
309309
"': " + annotatedMethods);
310310
}
311-
if ((this.beanFactory != null && !this.beanFactory.isSingleton(beanName)) ||
312-
(this.beanFactory instanceof SingletonBeanRegistry sbr && sbr.containsSingleton(beanName))) {
311+
if ((this.beanFactory != null &&
312+
(!this.beanFactory.containsBean(beanName) || !this.beanFactory.isSingleton(beanName)) ||
313+
(this.beanFactory instanceof SingletonBeanRegistry sbr && sbr.containsSingleton(beanName)))) {
313314
// Either a prototype/scoped bean or a FactoryBean with a pre-existing managed singleton
314315
// -> trigger manual cancellation when ContextClosedEvent comes in
315316
this.manualCancellationOnContextClose.add(bean);

spring-context/src/test/java/org/springframework/scheduling/annotation/ScheduledAnnotationBeanPostProcessorTests.java

+19
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import java.util.List;
3131
import java.util.Map;
3232
import java.util.Properties;
33+
import java.util.Set;
3334
import java.util.concurrent.TimeUnit;
3435

3536
import org.junit.jupiter.api.AfterEach;
@@ -294,6 +295,24 @@ void oneTimeTask() {
294295
assertThat(task.getInitialDelayDuration()).isEqualTo(Duration.ofMillis(2_000L));
295296
}
296297

298+
@Test
299+
void oneTimeTaskOnNonRegisteredBean() {
300+
BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class);
301+
context.registerBeanDefinition("postProcessor", processorDefinition);
302+
context.refresh();
303+
304+
ScheduledTaskHolder postProcessor = context.getBean("postProcessor", ScheduledTaskHolder.class);
305+
assertThat(postProcessor.getScheduledTasks()).hasSize(0);
306+
307+
Object target = context.getAutowireCapableBeanFactory().createBean(OneTimeTaskBean.class);
308+
assertThat(postProcessor.getScheduledTasks()).hasSize(1);
309+
@SuppressWarnings("unchecked")
310+
Set<Object> manualTasks = (Set<Object>)
311+
new DirectFieldAccessor(postProcessor).getPropertyValue("manualCancellationOnContextClose");
312+
assertThat(manualTasks).hasSize(1);
313+
assertThat(manualTasks).contains(target);
314+
}
315+
297316
@Test
298317
void cronTask() {
299318
BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class);

0 commit comments

Comments
 (0)