Avoid early singleton inference outside of original creation thread

See gh-23501
This commit is contained in:
Juergen Hoeller 2024-02-19 17:44:30 +01:00
parent 902e5707a8
commit 4a02893c31
2 changed files with 17 additions and 4 deletions

View File

@ -188,7 +188,10 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
singletonObject = this.earlySingletonObjects.get(beanName); singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) { if (singletonObject == null && allowEarlyReference) {
this.singletonLock.lock(); if (!this.singletonLock.tryLock()) {
// Avoid early singleton inference outside of original creation thread
return null;
}
try { try {
// Consistent creation of early reference within full singleton lock // Consistent creation of early reference within full singleton lock
singletonObject = this.singletonObjects.get(beanName); singletonObject = this.singletonObjects.get(beanName);

View File

@ -22,6 +22,9 @@ import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.testfixture.beans.TestBean; 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 * @author Juergen Hoeller
* @since 6.2 * @since 6.2
@ -31,8 +34,10 @@ class BeanFactoryLockingTests {
@Test @Test
void fallbackForThreadDuringInitialization() { void fallbackForThreadDuringInitialization() {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
beanFactory.registerBeanDefinition("bean1", new RootBeanDefinition(ThreadDuringInitialization.class)); beanFactory.registerBeanDefinition("bean1",
beanFactory.registerBeanDefinition("bean2", new RootBeanDefinition(TestBean.class)); new RootBeanDefinition(ThreadDuringInitialization.class));
beanFactory.registerBeanDefinition("bean2",
new RootBeanDefinition(TestBean.class, () -> new TestBean("tb")));
beanFactory.getBean(ThreadDuringInitialization.class); beanFactory.getBean(ThreadDuringInitialization.class);
} }
@ -51,7 +56,12 @@ class BeanFactoryLockingTests {
@Override @Override
public void afterPropertiesSet() throws Exception { public void afterPropertiesSet() throws Exception {
Thread thread = new Thread(() -> { 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; initialized = true;
}); });
thread.start(); thread.start();