Skip to content

Commit d2733ce

Browse files
committed
Notify lenientCreationFinished condition after locked creation as well
Closes gh-34522
1 parent 108caea commit d2733ce

File tree

2 files changed

+52
-14
lines changed

2 files changed

+52
-14
lines changed

Diff for: spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultSingletonBeanRegistry.java

+14-14
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,6 @@ public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
253253
Boolean lockFlag = isCurrentThreadAllowedToHoldSingletonLock();
254254
boolean acquireLock = !Boolean.FALSE.equals(lockFlag);
255255
boolean locked = (acquireLock && this.singletonLock.tryLock());
256-
boolean lenient = false;
257256
try {
258257
Object singletonObject = this.singletonObjects.get(beanName);
259258
if (singletonObject == null) {
@@ -268,7 +267,6 @@ public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
268267
Thread.currentThread().getName() + "\" while other thread holds " +
269268
"singleton lock for other beans " + this.singletonsCurrentlyInCreation);
270269
}
271-
lenient = true;
272270
this.lenientCreationLock.lock();
273271
try {
274272
this.singletonsInLenientCreation.add(beanName);
@@ -329,7 +327,7 @@ public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
329327
// Try late locking for waiting on specific bean to be finished.
330328
this.singletonLock.lock();
331329
locked = true;
332-
// Singleton object should have appeared in the meantime.
330+
// Lock-created singleton object should have appeared in the meantime.
333331
singletonObject = this.singletonObjects.get(beanName);
334332
if (singletonObject != null) {
335333
return singletonObject;
@@ -343,8 +341,12 @@ public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
343341
this.suppressedExceptions = new LinkedHashSet<>();
344342
}
345343
try {
346-
singletonObject = singletonFactory.getObject();
347-
newSingleton = true;
344+
// Leniently created singleton object could have appeared in the meantime.
345+
singletonObject = this.singletonObjects.get(beanName);
346+
if (singletonObject == null) {
347+
singletonObject = singletonFactory.getObject();
348+
newSingleton = true;
349+
}
348350
}
349351
catch (IllegalStateException ex) {
350352
// Has the singleton object implicitly appeared in the meantime ->
@@ -388,15 +390,13 @@ public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
388390
if (locked) {
389391
this.singletonLock.unlock();
390392
}
391-
if (lenient) {
392-
this.lenientCreationLock.lock();
393-
try {
394-
this.singletonsInLenientCreation.remove(beanName);
395-
this.lenientCreationFinished.signalAll();
396-
}
397-
finally {
398-
this.lenientCreationLock.unlock();
399-
}
393+
this.lenientCreationLock.lock();
394+
try {
395+
this.singletonsInLenientCreation.remove(beanName);
396+
this.lenientCreationFinished.signalAll();
397+
}
398+
finally {
399+
this.lenientCreationLock.unlock();
400400
}
401401
}
402402
}

Diff for: spring-context/src/test/java/org/springframework/context/annotation/BackgroundBootstrapTests.java

+38
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,16 @@ void bootstrapWithUnmanagedThreads() {
5656
ctx.close();
5757
}
5858

59+
@Test
60+
@Timeout(5)
61+
@EnabledForTestGroups(LONG_RUNNING)
62+
void bootstrapWithCircularReference() {
63+
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(CircularReferenceBeanConfig.class);
64+
ctx.getBean("testBean1", TestBean.class);
65+
ctx.getBean("testBean2", TestBean.class);
66+
ctx.close();
67+
}
68+
5969
@Test
6070
@Timeout(5)
6171
@EnabledForTestGroups(LONG_RUNNING)
@@ -138,6 +148,34 @@ public TestBean testBean4() {
138148
}
139149

140150

151+
@Configuration(proxyBeanMethods = false)
152+
static class CircularReferenceBeanConfig {
153+
154+
@Bean
155+
public TestBean testBean1(ObjectProvider<TestBean> testBean2) {
156+
new Thread(testBean2::getObject).start();
157+
try {
158+
Thread.sleep(1000);
159+
}
160+
catch (InterruptedException ex) {
161+
throw new RuntimeException(ex);
162+
}
163+
return new TestBean();
164+
}
165+
166+
@Bean
167+
public TestBean testBean2(TestBean testBean1) {
168+
try {
169+
Thread.sleep(2000);
170+
}
171+
catch (InterruptedException ex) {
172+
throw new RuntimeException(ex);
173+
}
174+
return new TestBean();
175+
}
176+
}
177+
178+
141179
@Configuration(proxyBeanMethods = false)
142180
static class CustomExecutorBeanConfig {
143181

0 commit comments

Comments
 (0)