44
44
import org .springframework .beans .factory .ListableBeanFactory ;
45
45
import org .springframework .beans .factory .SmartInitializingSingleton ;
46
46
import org .springframework .beans .factory .config .DestructionAwareBeanPostProcessor ;
47
+ import org .springframework .beans .factory .config .SingletonBeanRegistry ;
47
48
import org .springframework .beans .factory .support .MergedBeanDefinitionPostProcessor ;
48
49
import org .springframework .beans .factory .support .RootBeanDefinition ;
49
50
import org .springframework .context .ApplicationContext ;
@@ -155,6 +156,8 @@ public class ScheduledAnnotationBeanPostProcessor
155
156
156
157
private final Map <Object , List <Runnable >> reactiveSubscriptions = new IdentityHashMap <>(16 );
157
158
159
+ private final Set <Object > manualCancellationOnContextClose = Collections .newSetFromMap (new IdentityHashMap <>(16 ));
160
+
158
161
159
162
/**
160
163
* Create a default {@code ScheduledAnnotationBeanPostProcessor}.
@@ -305,6 +308,12 @@ public Object postProcessAfterInitialization(Object bean, String beanName) {
305
308
logger .trace (annotatedMethods .size () + " @Scheduled methods processed on bean '" + beanName +
306
309
"': " + annotatedMethods );
307
310
}
311
+ if ((this .beanFactory != null && !this .beanFactory .isSingleton (beanName )) ||
312
+ (this .beanFactory instanceof SingletonBeanRegistry sbr && sbr .containsSingleton (beanName ))) {
313
+ // Either a prototype/scoped bean or a FactoryBean with a pre-existing managed singleton
314
+ // -> trigger manual cancellation when ContextClosedEvent comes in
315
+ this .manualCancellationOnContextClose .add (bean );
316
+ }
308
317
}
309
318
}
310
319
return bean ;
@@ -595,6 +604,18 @@ public Set<ScheduledTask> getScheduledTasks() {
595
604
596
605
@ Override
597
606
public void postProcessBeforeDestruction (Object bean , String beanName ) {
607
+ cancelScheduledTasks (bean );
608
+ this .manualCancellationOnContextClose .remove (bean );
609
+ }
610
+
611
+ @ Override
612
+ public boolean requiresDestruction (Object bean ) {
613
+ synchronized (this .scheduledTasks ) {
614
+ return (this .scheduledTasks .containsKey (bean ) || this .reactiveSubscriptions .containsKey (bean ));
615
+ }
616
+ }
617
+
618
+ private void cancelScheduledTasks (Object bean ) {
598
619
Set <ScheduledTask > tasks ;
599
620
List <Runnable > liveSubscriptions ;
600
621
synchronized (this .scheduledTasks ) {
@@ -613,13 +634,6 @@ public void postProcessBeforeDestruction(Object bean, String beanName) {
613
634
}
614
635
}
615
636
616
- @ Override
617
- public boolean requiresDestruction (Object bean ) {
618
- synchronized (this .scheduledTasks ) {
619
- return (this .scheduledTasks .containsKey (bean ) || this .reactiveSubscriptions .containsKey (bean ));
620
- }
621
- }
622
-
623
637
@ Override
624
638
public void destroy () {
625
639
synchronized (this .scheduledTasks ) {
@@ -636,7 +650,10 @@ public void destroy() {
636
650
liveSubscription .run (); // equivalent to cancelling the subscription
637
651
}
638
652
}
653
+ this .reactiveSubscriptions .clear ();
654
+ this .manualCancellationOnContextClose .clear ();
639
655
}
656
+
640
657
this .registrar .destroy ();
641
658
if (this .localScheduler != null ) {
642
659
this .localScheduler .destroy ();
@@ -659,15 +676,10 @@ public void onApplicationEvent(ApplicationContextEvent event) {
659
676
finishRegistration ();
660
677
}
661
678
else if (event instanceof ContextClosedEvent ) {
662
- synchronized (this .scheduledTasks ) {
663
- Collection <Set <ScheduledTask >> allTasks = this .scheduledTasks .values ();
664
- for (Set <ScheduledTask > tasks : allTasks ) {
665
- for (ScheduledTask task : tasks ) {
666
- // At this early point, let in-progress tasks complete still
667
- task .cancel (false );
668
- }
669
- }
679
+ for (Object bean : this .manualCancellationOnContextClose ) {
680
+ cancelScheduledTasks (bean );
670
681
}
682
+ this .manualCancellationOnContextClose .clear ();
671
683
}
672
684
}
673
685
}
0 commit comments