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 da49209e89af..bc595b9036fc 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 @@ protected Object getSingleton(String beanName, boolean allowEarlyReference) { 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 e5966f675a19..519dac59fb23 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.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 @@ public void setBeanFactory(BeanFactory beanFactory) { @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();