From 4a02893c319e891da6c9e23dbc8f653460e51dc3 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Mon, 19 Feb 2024 17:44:30 +0100 Subject: [PATCH] Avoid early singleton inference outside of original creation thread See gh-23501 --- .../support/DefaultSingletonBeanRegistry.java | 5 ++++- .../beans/factory/BeanFactoryLockingTests.java | 16 +++++++++++++--- 2 files changed, 17 insertions(+), 4 deletions(-) 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 da49209e89a..bc595b9036f 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 @@ -188,7 +188,10 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { singletonObject = this.earlySingletonObjects.get(beanName); if (singletonObject == null && allowEarlyReference) { - this.singletonLock.lock(); + if (!this.singletonLock.tryLock()) { + // Avoid early singleton inference outside of original creation thread + return null; + } try { // Consistent creation of early reference within full singleton lock singletonObject = this.singletonObjects.get(beanName); diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/BeanFactoryLockingTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/BeanFactoryLockingTests.java index e5966f675a1..519dac59fb2 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/BeanFactoryLockingTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/BeanFactoryLockingTests.java @@ -22,6 +22,9 @@ import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.beans.testfixture.beans.TestBean; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + /** * @author Juergen Hoeller * @since 6.2 @@ -31,8 +34,10 @@ class BeanFactoryLockingTests { @Test void fallbackForThreadDuringInitialization() { DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); - beanFactory.registerBeanDefinition("bean1", new RootBeanDefinition(ThreadDuringInitialization.class)); - beanFactory.registerBeanDefinition("bean2", new RootBeanDefinition(TestBean.class)); + beanFactory.registerBeanDefinition("bean1", + new RootBeanDefinition(ThreadDuringInitialization.class)); + beanFactory.registerBeanDefinition("bean2", + new RootBeanDefinition(TestBean.class, () -> new TestBean("tb"))); beanFactory.getBean(ThreadDuringInitialization.class); } @@ -51,7 +56,12 @@ class BeanFactoryLockingTests { @Override public void afterPropertiesSet() throws Exception { Thread thread = new Thread(() -> { - beanFactory.getBean(TestBean.class); + // Fail for circular reference from other thread + assertThatExceptionOfType(BeanCurrentlyInCreationException.class).isThrownBy(() -> + beanFactory.getBean(ThreadDuringInitialization.class)); + // Leniently create unrelated other bean outside of singleton lock + assertThat(beanFactory.getBean(TestBean.class).getName()).isEqualTo("tb"); + // Creation attempt in other thread was successful initialized = true; }); thread.start();