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 acquireLock = !Boolean.FALSE.equals(lockFlag);
boolean locked = (acquireLock && this.singletonLock.tryLock());
boolean lenient = false;
try {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
@ -268,7 +267,6 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
Thread.currentThread().getName() + "\" while other thread holds " +
"singleton lock for other beans " + this.singletonsCurrentlyInCreation);
}
lenient = true;
this.lenientCreationLock.lock();
try {
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.
this.singletonLock.lock();
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);
if (singletonObject != null) {
return singletonObject;
@ -343,9 +341,13 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
this.suppressedExceptions = new LinkedHashSet<>();
}
try {
// Leniently created singleton object could have appeared in the meantime.
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
singletonObject = singletonFactory.getObject();
newSingleton = true;
}
}
catch (IllegalStateException ex) {
// Has the singleton object implicitly appeared in the meantime ->
// if yes, proceed with it since the exception indicates that state.
@ -388,7 +390,6 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
if (locked) {
this.singletonLock.unlock();
}
if (lenient) {
this.lenientCreationLock.lock();
try {
this.singletonsInLenientCreation.remove(beanName);
@ -399,7 +400,6 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
}
}
}
}
/**
* Determine whether the current thread is allowed to hold the singleton lock.

View File

@ -56,6 +56,16 @@ class BackgroundBootstrapTests {
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
@Timeout(5)
@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)
static class CustomExecutorBeanConfig {