Abstratc Bean Definition Reader
The AbstractBeanDefinitionReader acts as a base class for all bean definition readers. It provides common functionality, such as resource loading, and defines a template method that concrete readers must implement to perform the actual parsing of configuration data. It delegates the storage of parsed bean definitions to a BeanDefinitionRegistry instance.
package org.springframework.beans.factory.supper;
import org.springframework.beans.factory.BeansException;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.ResourceLoader;
public abstract class AbstractBeanDefinitionReader implements BeanDefinitionReader {
private final BeanDefinitionRegistry registry;
private ResourceLoader resourceLoader;
public AbstractBeanDefinitionReader(BeanDefinitionRegistry beanRegistry, ResourceLoader loader) {
this.registry = beanRegistry;
this.resourceLoader = loader;
}
public AbstractBeanDefinitionReader(BeanDefinitionRegistry registry) {
this(registry, new DefaultResourceLoader());
}
@Override
public BeanDefinitionRegistry getRegistry() {
return registry;
}
@Override
public void loadBeanDefinitionsFromLocations(String[] locations) throws BeansException {
for (String location : locations) {
loadBeanDefinitionsFromLocation(location);
}
}
public void setResourceLoaderForReading(ResourceLoader loader) {
this.resourceLoader = loader;
}
@Override
public ResourceLoader getResourceLoader() {
return resourceLoader;
}
}
XML Bean Definition Reader
The XmlBeanDefinitionReader is a concrete implementation of AbstractBeanDefinitionReader. Its primary responsibility is to parse an XML configuration file, extract bean definitions, and register them with the underlying BeanDefinitionRegistry.
package org.springframework.beans.factory.xml;
import cn.hutool.core.util.StrUtil;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.springframework.beans.PropertyValue;
import org.springframework.beans.factory.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanReference;
import org.springframework.beans.factory.supper.AbstractBeanDefinitionReader;
import org.springframework.beans.factory.supper.BeanDefinitionRegistry;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
public static final String BEAN_ELEMENT_TAG = "bean";
public static final String PROPERTY_ELEMENT_TAG = "property";
public static final String IDENTIFIER_ATTRIBUTE = "id";
public static final String ALIAS_ATTRIBUTE = "name";
public static final String CLASS_NAME_ATTRIBUTE = "class";
public static final String VALUE_ATTRIBUTE_NAME = "value";
public static final String REFERENCE_ATTRIBUTE_NAME = "ref";
public static final String INIT_METHOD_ATTRIBUTE = "init-method";
public static final String DESTROY_METHOD_ATTRIBUTE = "destroy-method";
public static final String SCOPE_ATTRIBUTE = "scope";
public static final String LAZYINIT_ATTRIBUTE = "lazyInit";
public static final String BASE_PACKAGE_ATTRIBUTE = "base-package";
public static final String COMPONENT_SCAN_ELEMENT = "component-scan";
public XmlBeanDefinitionReader(BeanDefinitionRegistry registry) {
super(registry);
}
public XmlBeanDefinitionReader(BeanDefinitionRegistry registry, ResourceLoader resourceLoader) {
super(registry, resourceLoader);
}
@Override
public void loadDefinitionsFromResource(Resource resource) throws BeansException {
try (InputStream inputStream = resource.getInputStream()) {
parseXmlStream(inputStream);
} catch (IOException | DocumentException ex) {
throw new BeansException("Error reading XML document from " + resource, ex);
}
}
@Override
public void loadDefinitionsFromLocation(String location) throws BeansException {
ResourceLoader loader = getResourceLoader();
Resource resource = loader.getResource(location);
loadDefinitionsFromResource(resource);
}
public void parseXmlStream(InputStream inputStream) throws DocumentException {
SAXReader reader = new SAXReader();
Document document = reader.read(inputStream);
Element root = document.getRootElement();
// Handle component scanning if present
Element scanComponent = root.element(COMPONENT_SCAN_ELEMENT);
if (scanComponent != null) {
String scanPath = scanComponent.attributeValue(BASE_PACKAGE_ATTRIBUTE);
if (StrUtil.isNotEmpty(scanPath)) {
processComponentScan(scanPath);
}
}
// Process each bean definition
List<element> beanElements = root.elements(BEAN_ELEMENT_TAG);
for (Element beanElement : beanElements) {
String beanIdentifier = beanElement.attributeValue(IDENTIFIER_ATTRIBUTE);
String beanAlias = beanElement.attributeValue(ALIAS_ATTRIBUTE);
String beanClassName = beanElement.attributeValue(CLASS_NAME_ATTRIBUTE);
String initMethodName = beanElement.attributeValue(INIT_METHOD_ATTRIBUTE);
String destroyMethodName = beanElement.attributeValue(DESTROY_METHOD_ATTRIBUTE);
String beanScope = beanElement.attributeValue(SCOPE_ATTRIBUTE);
String lazyInitFlag = beanElement.attributeValue(LAZYINIT_ATTRIBUTE);
Class> beanClass;
try {
beanClass = Class.forName(beanClassName);
} catch (ClassNotFoundException e) {
throw new BeansException("Class not found: [" + beanClassName + "]");
}
// Prefer 'id' over 'name' for the bean name
beanAlias = StrUtil.isNotEmpty(beanIdentifier) ? beanIdentifier : beanAlias;
if (StrUtil.isEmpty(beanAlias)) {
beanAlias = StrUtil.lowerFirst(beanClass.getSimpleName());
}
// Create a new bean definition
BeanDefinition definition = new BeanDefinition(beanClass);
// Configure initialization and destruction methods
definition.setInitMethodName(initMethodName);
definition.setDestroyMethodName(destroyMethodName);
// Set bean scope if specified
if (StrUtil.isNotEmpty(beanScope)) {
definition.setScope(beanScope);
}
// Process and set bean properties
List<element> propertyElements = beanElement.elements(PROPERTY_ELEMENT_TAG);
for (Element propertyItem : propertyElements) {
String propertyName = propertyItem.attributeValue(NAME_ATTRIBUTE);
String propertyValue = propertyItem.attributeValue(VALUE_ATTRIBUTE_NAME);
String propertyRef = propertyItem.attributeValue(REFERENCE_ATTRIBUTE_NAME);
if (StrUtil.isEmpty(propertyName)) {
throw new BeansException("Property name attribute cannot be null or empty");
}
Object valueToSet = propertyValue;
if (StrUtil.isNotEmpty(propertyRef)) {
valueToSet = new BeanReference(propertyRef);
}
PropertyValue propertyValueObj = new PropertyValue(propertyName, valueToSet);
definition.getPropertyValues().addPropertyValues(propertyValueObj);
}
// Ensure bean names are unique
if (getRegistry().containsBeanDefinition(beanAlias)) {
throw new BeansException("Duplicate bean name not allowed: [" + beanAlias + "]");
}
// Register the bean definition with the registry
getRegistry().registerBeanDefinition(beanAlias, definition);
}
}
/**
* Handles the component-scan element to automatically register beans.
* This is a placeholder for component scanning logic.
*
* @param scanPath The base package to scan for components.
*/
public void processComponentScan(String scanPath) {
// Implementation would go here
}
}
</element></element>