Notify lenientCreationFinished condition after locked creation as well

Closes gh-34522
This commit is contained in:
Juergen Hoeller 2025-03-01 22:20:23 +01:00
parent 108caea385
commit d2733cea36
2 changed files with 52 additions and 14 deletions

View File

@ -253,7 +253,6 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
Boolean lockFlag = isCurrentThreadAllowedToHoldSingletonLock(); Boolean lockFlag = isCurrentThreadAllowedToHoldSingletonLock();
boolean acquireLock = !Boolean.FALSE.equals(lockFlag); boolean acquireLock = !Boolean.FALSE.equals(lockFlag);
boolean locked = (acquireLock && this.singletonLock.tryLock()); boolean locked = (acquireLock && this.singletonLock.tryLock());
boolean lenient = false;
try { try {
Object singletonObject = this.singletonObjects.get(beanName); Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) { if (singletonObject == null) {
@ -268,7 +267,6 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
Thread.currentThread().getName() + "\" while other thread holds " + Thread.currentThread().getName() + "\" while other thread holds " +
"singleton lock for other beans " + this.singletonsCurrentlyInCreation); "singleton lock for other beans " + this.singletonsCurrentlyInCreation);
} }
lenient = true;
this.lenientCreationLock.lock(); this.lenientCreationLock.lock();
try { try {
this.singletonsInLenientCreation.add(beanName); this.singletonsInLenientCreation.add(beanName);
@ -329,7 +327,7 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
// Try late locking for waiting on specific bean to be finished. // Try late locking for waiting on specific bean to be finished.
this.singletonLock.lock(); this.singletonLock.lock();
locked = true; locked = true;
// Singleton object should have appeared in the meantime. // Lock-created singleton object should have appeared in the meantime.
singletonObject = this.singletonObjects.get(beanName); singletonObject = this.singletonObjects.get(beanName);
if (singletonObject != null) { if (singletonObject != null) {
return singletonObject; return singletonObject;
@ -343,8 +341,12 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
this.suppressedExceptions = new LinkedHashSet<>(); this.suppressedExceptions = new LinkedHashSet<>();
} }
try { try {
singletonObject = singletonFactory.getObject(); // Leniently created singleton object could have appeared in the meantime.
newSingleton = true; singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
singletonObject = singletonFactory.getObject();
newSingleton = true;
}
} }
catch (IllegalStateException ex) { catch (IllegalStateException ex) {
// Has the singleton object implicitly appeared in the meantime -> // Has the singleton object implicitly appeared in the meantime ->
@ -388,15 +390,13 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
if (locked) { if (locked) {
this.singletonLock.unlock(); this.singletonLock.unlock();
} }
if (lenient) { this.lenientCreationLock.lock();
this.lenientCreationLock.lock(); try {
try { this.singletonsInLenientCreation.remove(beanName);
this.singletonsInLenientCreation.remove(beanName); this.lenientCreationFinished.signalAll();
this.lenientCreationFinished.signalAll(); }
} finally {
finally { this.lenientCreationLock.unlock();
this.lenientCreationLock.unlock();
}
} }
} }
} }

View File

@ -56,6 +56,16 @@ class BackgroundBootstrapTests {
ctx.close(); ctx.close();
} }
@Test
@Timeout(5)
@EnabledForTestGroups(LONG_RUNNING)
void bootstrapWithCircularReference() {
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(CircularReferenceBeanConfig.class);
ctx.getBean("testBean1", TestBean.class);
ctx.getBean("testBean2", TestBean.class);
ctx.close();
}
@Test @Test
@Timeout(5) @Timeout(5)
@EnabledForTestGroups(LONG_RUNNING) @EnabledForTestGroups(LONG_RUNNING)
@ -138,6 +148,34 @@ class BackgroundBootstrapTests {
} }
@Configuration(proxyBeanMethods = false)
static class CircularReferenceBeanConfig {
@Bean
public TestBean testBean1(ObjectProvider<TestBean> testBean2) {
new Thread(testBean2::getObject).start();
try {
Thread.sleep(1000);
}
catch (InterruptedException ex) {
throw new RuntimeException(ex);
}
return new TestBean();
}
@Bean
public TestBean testBean2(TestBean testBean1) {
try {
Thread.sleep(2000);
}
catch (InterruptedException ex) {
throw new RuntimeException(ex);
}
return new TestBean();
}
}
@Configuration(proxyBeanMethods = false) @Configuration(proxyBeanMethods = false)
static class CustomExecutorBeanConfig { static class CustomExecutorBeanConfig {