diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultSingletonBeanRegistry.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultSingletonBeanRegistry.java index 8cd40e2aa8..44a05756ff 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultSingletonBeanRegistry.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultSingletonBeanRegistry.java @@ -99,13 +99,13 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements @Nullable private volatile Thread singletonCreationThread; + /** Flag that indicates whether we're currently within destroySingletons. */ + private volatile boolean singletonsCurrentlyInDestruction = false; + /** Collection of suppressed Exceptions, available for associating related causes. */ @Nullable private Set suppressedExceptions; - /** Flag that indicates whether we're currently within destroySingletons. */ - private boolean singletonsCurrentlyInDestruction = false; - /** Disposable bean instances: bean name to disposable instance. */ private final Map disposableBeans = new LinkedHashMap<>(); @@ -562,13 +562,7 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements if (logger.isTraceEnabled()) { logger.trace("Destroying singletons in " + this); } - this.singletonLock.lock(); - try { - this.singletonsCurrentlyInDestruction = true; - } - finally { - this.singletonLock.unlock(); - } + this.singletonsCurrentlyInDestruction = true; String[] disposableBeanNames; synchronized (this.disposableBeans) { @@ -610,21 +604,28 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements * @see #destroyBean */ public void destroySingleton(String beanName) { - // Remove a registered singleton of the given name, if any. - this.singletonLock.lock(); - try { - removeSingleton(beanName); - } - finally { - this.singletonLock.unlock(); - } - // Destroy the corresponding DisposableBean instance. + // This also triggers the destruction of dependent beans. DisposableBean disposableBean; synchronized (this.disposableBeans) { disposableBean = this.disposableBeans.remove(beanName); } destroyBean(beanName, disposableBean); + + // destroySingletons() removes all singleton instances at the end, + // leniently tolerating late retrieval during the shutdown phase. + if (!this.singletonsCurrentlyInDestruction) { + // For an individual destruction, remove the registered instance now. + // As of 6.2, this happens after the current bean's destruction step, + // allowing for late bean retrieval by on-demand suppliers etc. + this.singletonLock.lock(); + try { + removeSingleton(beanName); + } + finally { + this.singletonLock.unlock(); + } + } } /** diff --git a/spring-context/src/test/java/org/springframework/context/support/Service.java b/spring-context/src/test/java/org/springframework/context/support/Service.java index 0c177d36fe..116488c983 100644 --- a/spring-context/src/test/java/org/springframework/context/support/Service.java +++ b/spring-context/src/test/java/org/springframework/context/support/Service.java @@ -85,12 +85,11 @@ public class Service implements ApplicationContextAware, MessageSourceAware, Dis Assert.state(applicationContext.getBean("messageSource") instanceof StaticMessageSource, "Invalid MessageSource bean"); try { + // Should not throw BeanCreationNotAllowedException on 6.2 anymore applicationContext.getBean("service2"); - // Should have thrown BeanCreationNotAllowedException - properlyDestroyed = false; } catch (BeanCreationNotAllowedException ex) { - // expected + properlyDestroyed = false; } }); thread.start();