Skip to content

Commit 4a02893

Browse files
committed
Avoid early singleton inference outside of original creation thread
See gh-23501
1 parent 902e570 commit 4a02893

File tree

2 files changed

+17
-4
lines changed

2 files changed

+17
-4
lines changed

spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultSingletonBeanRegistry.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,10 @@ protected Object getSingleton(String beanName, boolean allowEarlyReference) {
188188
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
189189
singletonObject = this.earlySingletonObjects.get(beanName);
190190
if (singletonObject == null && allowEarlyReference) {
191-
this.singletonLock.lock();
191+
if (!this.singletonLock.tryLock()) {
192+
// Avoid early singleton inference outside of original creation thread
193+
return null;
194+
}
192195
try {
193196
// Consistent creation of early reference within full singleton lock
194197
singletonObject = this.singletonObjects.get(beanName);

spring-beans/src/test/java/org/springframework/beans/factory/BeanFactoryLockingTests.java

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@
2222
import org.springframework.beans.factory.support.RootBeanDefinition;
2323
import org.springframework.beans.testfixture.beans.TestBean;
2424

25+
import static org.assertj.core.api.Assertions.assertThat;
26+
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
27+
2528
/**
2629
* @author Juergen Hoeller
2730
* @since 6.2
@@ -31,8 +34,10 @@ class BeanFactoryLockingTests {
3134
@Test
3235
void fallbackForThreadDuringInitialization() {
3336
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
34-
beanFactory.registerBeanDefinition("bean1", new RootBeanDefinition(ThreadDuringInitialization.class));
35-
beanFactory.registerBeanDefinition("bean2", new RootBeanDefinition(TestBean.class));
37+
beanFactory.registerBeanDefinition("bean1",
38+
new RootBeanDefinition(ThreadDuringInitialization.class));
39+
beanFactory.registerBeanDefinition("bean2",
40+
new RootBeanDefinition(TestBean.class, () -> new TestBean("tb")));
3641
beanFactory.getBean(ThreadDuringInitialization.class);
3742
}
3843

@@ -51,7 +56,12 @@ public void setBeanFactory(BeanFactory beanFactory) {
5156
@Override
5257
public void afterPropertiesSet() throws Exception {
5358
Thread thread = new Thread(() -> {
54-
beanFactory.getBean(TestBean.class);
59+
// Fail for circular reference from other thread
60+
assertThatExceptionOfType(BeanCurrentlyInCreationException.class).isThrownBy(() ->
61+
beanFactory.getBean(ThreadDuringInitialization.class));
62+
// Leniently create unrelated other bean outside of singleton lock
63+
assertThat(beanFactory.getBean(TestBean.class).getName()).isEqualTo("tb");
64+
// Creation attempt in other thread was successful
5565
initialized = true;
5666
});
5767
thread.start();

0 commit comments

Comments
 (0)