Merge branch '6.2.x'

This commit is contained in:
Juergen Hoeller 2025-03-01 22:21:39 +01:00
commit 533ecf0244
2 changed files with 52 additions and 14 deletions

View File

@ -251,7 +251,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) {
@ -266,7 +265,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);
@ -327,7 +325,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;
@ -341,8 +339,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 ->
@ -386,15 +388,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 {