Building a Minimalist Bean Container
Understanding the Bean Container Concept
A Spring Bean container is responsible for managing the configuration and lifecycle of application objects. It allows you to define how each bean is created—whether as a singleton instance or a new instance per request—and how beans relate to and interact with each other.
When a bean is managed by the Spring container, its configuration is decomposed and stored in a bean definition. This decouples the object from its instantiation logic, making it easier for Spring to handle complex scenarios like circular dependencies.
After a bean is defined and registered, Spring handles the assembly process, including initialization and property population, resulting in a fully constructed bean instance ready for use.
Design Approach
Any data structure that stores information can be considered a container. For a Spring Bean container, we need a structure that supports name-based indexing and retrieval. A HashMap is ideal for this purpose.
HashMap uses techniques like perturbation functions, load factors, and red-black tree conversions to create a chained addressing data structure. This ensures data is distributed across hash buckets, with lookups typically ranging from O(1) to O(log n) in complexity. Even under extreme conditions with longer linked lists, performance remains acceptable for bean management.
A basic bean container implementation requires three core steps:
- Definition: Creating a BeanDefinition object to hold bean metadata.
- Registration: Storing bean definitions in a container.
- Retrieval: Fetching bean instances by name.
Implementation
1. Project Structure
basic-bean-container
└── src
├── main
│ └── java
│ └── com
│ └── example
│ └── container
│ ├── BeanDefinition.java
│ └── BeanRegistry.java
└── test
└── java
└── com
└── example
└── container
├── beans
│ └── SampleService.java
└── ContainerTest.java
2. Bean Definition Class
public class BeanDefinition {
private Object beanInstance;
public BeanDefinition(Object beanInstance) {
this.beanInstance = beanInstance;
}
public Object getBeanInstance() {
return beanInstance;
}
}
This simplified BeanDefinition currently stores only the bean object. In a full implementation, it would include scope (singleton/prototype), bean class, and role information.
3. Bean Registry Class
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class BeanRegistry {
private Map<String, BeanDefinition> beanDefinitions = new ConcurrentHashMap<>();
public Object getBean(String beanName) {
BeanDefinition definition = beanDefinitions.get(beanName);
if (definition == null) {
throw new RuntimeException("No bean found with name: " + beanName);
}
return definition.getBeanInstance();
}
public void registerBean(String beanName, BeanDefinition definition) {
beanDefinitions.put(beanName, definition);
}
}
The BeanRegistry handles both registration and retrieval opreations. While this implementation is minimal, it demonstrates the core pattern used throughout Spring's container architecture.
4. Usage Example
// Define a simple service
public class SampleService {
private String serviceName = "DefaultService";
public String execute() {
return "Service executed: " + serviceName;
}
}
// Test the container
public class ContainerTest {
public static void main(String[] args) {
// Create bean instance
SampleService service = new SampleService();
// Create bean definition
BeanDefinition definition = new BeanDefinition(service);
// Register bean
BeanRegistry registry = new BeanRegistry();
registry.registerBean("sampleService", definition);
// Retrieve and use bean
SampleService retrievedService = (SampleService) registry.getBean("sampleService");
System.out.println(retrievedService.execute());
}
}