Keywords: Spring Framework | Bean Creation Exception | JUnit Testing
Abstract: This paper provides an in-depth exploration of the common BeanCreationNotAllowedException in the Spring framework, particularly the "Singleton bean creation not allowed while the singletons of this factory are in destruction" error. By analyzing typical scenarios in JUnit testing environments and integrating best practice solutions, it systematically examines the root causes, triggering mechanisms, and multiple resolution strategies. The article not only explains core concepts such as Java environment configuration, multi-threading timing, and BeanFactory lifecycle in detail but also offers code examples and debugging recommendations to help developers prevent and resolve such issues fundamentally.
Exception Phenomenon and Background
During JUnit test execution in Spring applications, developers often encounter the org.springframework.beans.factory.BeanCreationNotAllowedException exception, with typical error messages like: Error creating bean with name 'somarFactory': Singleton bean creation not allowed while the singletons of this factory are in destruction (Do not request a bean from a BeanFactory in a destroy method implementation!). This indicates an attempt to create a new singleton bean during the BeanFactory's destruction phase, violating Spring container lifecycle management rules.
Core Cause Analysis
The fundamental cause of this exception lies in BeanFactory state management conflicts. During the destruction phase, the Spring container closes all singleton beans, placing it in a "destruction in progress" state where creating new singleton beans is prohibited. This typically occurs in scenarios such as:
- Improper test environment configuration leading to multiple container instance conflicts
- Incorrect Java environment variable settings (e.g.,
JAVA_HOME) or incompatible JDK versions - Premature container closure (
context.close()) in multi-threaded environments before all bean invocations complete - Attempting to obtain new bean instances within
@Destroymethods or destruction callbacks
Primary Solutions
Based on best practices and community experience, resolving this issue requires a multi-dimensional approach:
1. Environment Configuration Check
Ensure the JAVA_HOME environment variable correctly points to a valid JDK installation path. For example, in Unix/Linux systems:
export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64
export PATH=$JAVA_HOME/bin:$PATHSimultaneously verify JDK version compatibility, recommending Spring-officially supported versions (e.g., JDK 8 or 11). This can be checked with:
System.out.println("Java version: " + System.getProperty("java.version"));
System.out.println("Java home: " + System.getProperty("java.home"));2. Container Instance Management
In testing environments, ensure only one Spring container instance is running. Avoid multiple BeanFactory coexistence due to repeated Tomcat or other server startups. For example, correctly using the @SpringBootTest annotation in JUnit tests:
@SpringBootTest
@RunWith(SpringRunner.class)
public class SomarFactoryTest {
@Autowired
private ApplicationContext context;
@Test
public void testBeanCreation() {
// Test logic
}
}3. Lifecycle Control
In multi-threaded scenarios, ensure all bean invocations complete before closing the container. Avoid calling context.close() in unfinished threads. Example:
ExecutorService executor = Executors.newFixedThreadPool(5);
List<Future> futures = new ArrayList<>();
for (int i = 0; i < 10; i++) {
futures.add(executor.submit(() -> {
// Invoke bean method
somarFactory.doSomething();
}));
}
// Wait for all tasks to complete
for (Future future : futures) {
future.get();
}
executor.shutdown();
// Now safe to close container
// context.close(); // Determine based on actual situationIn-Depth Technical Details
During the destruction phase, Spring's BeanFactory iterates through all singleton beans, invoking their destruction methods. This process is unidirectional and irreversible; once in destruction state, any request to create new singletons triggers an exception. The internal mechanism can be understood through this pseudo-code:
public class DefaultSingletonBeanRegistry {
private boolean singletonsCurrentlyInDestruction = false;
protected void beforeSingletonCreation(String beanName) {
if (this.singletonsCurrentlyInDestruction) {
throw new BeanCreationNotAllowedException(beanName,
"Singleton bean creation not allowed while singletons are in destruction");
}
// ... Other check logic
}
public void destroySingletons() {
this.singletonsCurrentlyInDestruction = true;
try {
// Destroy all singleton beans
for (String beanName : this.singletonNames) {
destroySingleton(beanName);
}
} finally {
this.singletonsCurrentlyInDestruction = false;
}
}
}This design ensures container state consistency, preventing unpredictable bean dependency relationships during destruction.
Prevention and Best Practices
To avoid such exceptions, follow these development guidelines:
- Explicitly specify container configuration in test classes to prevent implicit creation of multiple ApplicationContexts
- Use the
@DirtiesContextannotation to manage container state between tests - Perform only resource cleanup in destruction methods, avoiding triggering new bean acquisitions
- Regularly update Spring versions to fix known lifecycle management defects
- Simulate complete startup-run-shutdown workflows when writing integration tests
By systematically understanding Spring container lifecycle management and state transition mechanisms, developers can more effectively diagnose and resolve BeanCreationNotAllowedException exceptions, enhancing application stability and maintainability.