In a lightweight Spring implementation, the bean definition mechanism consists of several core classes that work together to manage object instantiation and dependency injection. This article explores the fundamental components that form the foundation of Spring's container architecture.
PropertyValue Class
The PropertyValue class represents a single property configuration for a bean. It encapsulates both the property name and its corresponding value, serving as the atomic unit of bean configuration.
public class PropertyValue {
private final String propertyName;
private final Object propertyValue;
public PropertyValue(String propertyName, Object propertyValue) {
this.propertyName = propertyName;
this.propertyValue = propertyValue;
}
public String getPropertyName() {
return propertyName;
}
public Object getPropertyValue() {
return propertyValue;
}
}
PropertyValues Container
PropertyValues acts as a collection container that holds multiple PropertyValue instances. It provides methods to add properties and retrieve all configured properties for a bean.
public class PropertyValues {
private final List<PropertyValue> properties = new ArrayList<>();
public void addPropertyValue(PropertyValue pv) {
// Replace existing property with the same name
for (int i = 0; i < properties.size(); i++) {
PropertyValue existing = properties.get(i);
if (existing.getPropertyName().equals(pv.getPropertyName())) {
properties.set(i, pv);
return;
}
}
// Add new property if no duplicate found
properties.add(pv);
}
public PropertyValue[] getAllProperties() {
return this.properties.toArray(new PropertyValue[0]);
}
}
BeanDefinition Implementation
BeanDefinition serves as the metadata holder for bean configuration. It stores the class type, property values, initialization and destruction callbacks, scope configuration, and lazy initialization flags.
public class BeanDefinition {
public static final String SCOPE_SINGLETON = "singleton";
private static final String SCOPE_PROTOTYPE = "prototype";
private Class beanType;
private PropertyValues propertyValues;
private String initMethodName;
private String destroyMethodName;
private String scope = SCOPE_SINGLETON;
private boolean singleton = true;
private boolean prototype = false;
private boolean lazyInit = false;
public BeanDefinition(Class beanType) {
this(beanType, null);
}
public BeanDefinition(Class beanType, PropertyValues propertyValues) {
this.beanType = beanType;
this.propertyValues = propertyValues != null ? propertyValues : new PropertyValues();
}
public void setScope(String scope) {
this.scope = scope;
this.singleton = SCOPE_SINGLETON.equals(scope);
this.prototype = SCOPE_PROTOTYPE.equals(scope);
}
public boolean isSingleton() {
return singleton;
}
public boolean isPrototype() {
return prototype;
}
public void setPropertyValues(PropertyValues propertyValues) {
this.propertyValues = propertyValues;
}
public PropertyValues getPropertyValues() {
return propertyValues;
}
public Class getBeanType() {
return beanType;
}
public String getInitMethodName() {
return initMethodName;
}
public void setInitMethodName(String initMethodName) {
this.initMethodName = initMethodName;
}
public String getDestroyMethodName() {
return destroyMethodName;
}
public void setDestroyMethodName(String destroyMethodName) {
this.destroyMethodName = destroyMethodName;
}
public void setLazyInit(boolean lazyInit) {
this.lazyInit = lazyInit;
}
public boolean isLazyInit() {
return lazyInit;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
BeanDefinition that = (BeanDefinition) o;
return beanType.equals(that.beanType);
}
@Override
public int hashCode() {
return Objects.hash(beanType);
}
}
BeanDefinitionRegistry Enterface
The registry interface defines the contract for storing and retrieving bean definitions. It abstracts the underlying storage mechanism, allowing different implementations.
public interface BeanDefinitionRegistry {
void registerBeanDefinition(String beanName, BeanDefinition definition);
BeanDefinition getBeanDefinition(String beanName) throws BeansException;
boolean containsBeanDefinition(String beanName);
}
DefaultListableBeanFactory Implementation
DefaultListableBeanFactory provides the core bean container functionality by implementing BeanDefinitionRegistry. Internally, it maintains a ConcurrentHashMap to store bean definitions thread-safely.
public class DefaultListableBeanFactory implements BeanDefinitionRegistry {
private Map<String, BeanDefinition> definitionCache = new ConcurrentHashMap<>();
@Override
public void registerBeanDefinition(String beanName, BeanDefinition definition) {
definitionCache.put(beanName, definition);
}
@Override
public BeanDefinition getBeanDefinition(String beanName) throws BeansException {
BeanDefinition definition = definitionCache.get(beanName);
if (definition == null) {
throw new BeansException("Bean definition not found for: " + beanName);
}
return definition;
}
@Override
public boolean containsBeanDefinition(String beanName) {
return definitionCache.containsKey(beanName);
}
}
Practical Test Example
public class BeanDefinitionRegistryTest {
@Test
public void testBeanDefinitionStorage() throws Exception {
// Configure property values for the bean
PropertyValues config = new PropertyValues();
config.addPropertyValue(new PropertyValue("age", 50));
config.addPropertyValue(new PropertyValue("name", "John"));
// Create bean definition with class and properties
BeanDefinition definition = new BeanDefinition(UserService.class, config);
// Register the bean definition in the factory
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
factory.registerBeanDefinition("userService", definition);
// Retrieve the registered bean definition
BeanDefinition retrieved = factory.getBeanDefinition("userService");
assertNotNull(retrieved);
assertEquals(UserService.class, retrieved.getBeanType());
}
}
The architecture described above establishes the foundation for Spring's IoC container, providing mechanisms to register, store, and retrieve bean metadata. The separation between configuration (PropertyValues), metadata (BeanDefinition), and storage (BeanDefinitionRegistry) allows for flexible and extensible bean management.