-
Notifications
You must be signed in to change notification settings - Fork 38.8k
Closed
Labels
in: coreIssues in core modules (aop, beans, core, context, expression)Issues in core modules (aop, beans, core, context, expression)type: regressionA bug that is also a regressionA bug that is also a regression
Milestone
Description
Affects: 6.2.0-M7
After updating to the latest milestone version (6.2.0-M7), an org.springframework.beans.factory.BeanCurrentlyInCreationException is thrown when getting a bean from beanFactory of StaticApplicationContext. Race conditions take place when multiple threads are involved.
Apparently, DefaultSingletonBeanRegistry#getSingleton(String beanName, ObjectFactory<?> singletonFactory) is the method where the following scenario takes place:
beforeSingletonCreation(beanName)is called in Thread A,beforeSingletonCreation(beanName)is called in Thread B,- Because
afterSingletonCreation(String beanName)has not yet been called in Thread A, the above step will cause race condition and throwing ofBeanCurrentlyInCreationException.
Notes
- In version 6.1.11 the exception is not raised.
- When using
DefaultSingletonBeanRegistrydirectly instead of throughStaticApplicationContext, then no exception is thrown.
Minimal example (might require several runs)
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.BeanCurrentlyInCreationException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.support.StaticApplicationContext;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertThrows;
class BeanFactoryRaceConditionTest {
private final ExecutorService executorService = Executors.newFixedThreadPool(10);
@Test
void testRaceCondition() {
StaticApplicationContext applicationContext = new StaticApplicationContext();
applicationContext.registerSingleton("book", Book.class);
BeanFactory beanFactory = applicationContext.getAutowireCapableBeanFactory();
for (int i = 0; i < 10; i++) {
executorService.execute(() -> {
for (int j = 0; j < 1000; j++) {
beanFactory.getBean("book");
}
});
}
assertThrows(BeanCurrentlyInCreationException.class, () -> {
for (int i = 0; i < 1000; i++) {
beanFactory.getBean("book");
}
});
}
@Test
void testNoRaceCondition() {
ExecutorService executorService = Executors.newFixedThreadPool(10);
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
beanFactory.registerSingleton("book", Book.class);
for (int i = 0; i < 10; i++) {
executorService.execute(() -> {
for (int j = 0; j < 1000; j++) {
beanFactory.getBean("book");
}
});
}
assertDoesNotThrow(()->{
for (int i = 0; i < 1000; i++) {
beanFactory.getBean("book");
}
});
}
static class Book {
}
}Traces for testRaceCondition
Exception in thread "pool-1-thread-5" org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'book': Requested bean is currently in creation: Is there an unresolvable circular reference or an asynchronous initialization dependency?
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.beforeSingletonCreation(DefaultSingletonBeanRegistry.java:424)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:288)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:334)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
at com.example.demo.BeanFactoryRaceConditionTest.lambda$testRaceCondition$0(BeanFactoryRaceConditionTest.java:23)
Metadata
Metadata
Assignees
Labels
in: coreIssues in core modules (aop, beans, core, context, expression)Issues in core modules (aop, beans, core, context, expression)type: regressionA bug that is also a regressionA bug that is also a regression