Skip to content

Commit

Permalink
Avoid early singleton inference outside of original creation thread
Browse files Browse the repository at this point in the history
  • Loading branch information
jhoeller committed Feb 19, 2024
1 parent 902e570 commit 4a02893
Show file tree
Hide file tree
Showing 2 changed files with 17 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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);
}

Expand All @@ -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();
Expand Down

0 comments on commit 4a02893

Please sign in to comment.