4444import  org .springframework .beans .factory .ListableBeanFactory ;
4545import  org .springframework .beans .factory .SmartInitializingSingleton ;
4646import  org .springframework .beans .factory .config .DestructionAwareBeanPostProcessor ;
47+ import  org .springframework .beans .factory .config .SingletonBeanRegistry ;
4748import  org .springframework .beans .factory .support .MergedBeanDefinitionPostProcessor ;
4849import  org .springframework .beans .factory .support .RootBeanDefinition ;
4950import  org .springframework .context .ApplicationContext ;
@@ -155,6 +156,8 @@ public class ScheduledAnnotationBeanPostProcessor
155156
156157private  final  Map <Object , List <Runnable >> reactiveSubscriptions  = new  IdentityHashMap <>(16 );
157158
159+ private  final  Set <Object > manualCancellationOnContextClose  = Collections .newSetFromMap (new  IdentityHashMap <>(16 ));
160+ 
158161
159162/** 
160163 * Create a default {@code ScheduledAnnotationBeanPostProcessor}. 
@@ -305,6 +308,12 @@ public Object postProcessAfterInitialization(Object bean, String beanName) {
305308logger .trace (annotatedMethods .size () + " @Scheduled methods processed on bean '"  + beanName  +
306309"': "  + annotatedMethods );
307310}
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+ }
308317}
309318}
310319return  bean ;
@@ -595,6 +604,18 @@ public Set<ScheduledTask> getScheduledTasks() {
595604
596605@ Override 
597606public  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 ) {
598619Set <ScheduledTask > tasks ;
599620List <Runnable > liveSubscriptions ;
600621synchronized  (this .scheduledTasks ) {
@@ -613,13 +634,6 @@ public void postProcessBeforeDestruction(Object bean, String beanName) {
613634}
614635}
615636
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- 
623637@ Override 
624638public  void  destroy () {
625639synchronized  (this .scheduledTasks ) {
@@ -636,7 +650,10 @@ public void destroy() {
636650liveSubscription .run (); // equivalent to cancelling the subscription 
637651}
638652}
653+ this .reactiveSubscriptions .clear ();
654+ this .manualCancellationOnContextClose .clear ();
639655}
656+ 
640657this .registrar .destroy ();
641658if  (this .localScheduler  != null ) {
642659this .localScheduler .destroy ();
@@ -659,15 +676,10 @@ public void onApplicationEvent(ApplicationContextEvent event) {
659676finishRegistration ();
660677}
661678else  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 );
670681}
682+ this .manualCancellationOnContextClose .clear ();
671683}
672684}
673685}
0 commit comments