Building a Simple XML-based Bean Container in Java

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>

Tags: java Spring Framework Dependency Injection XML beanfactory

Posted on Fri, 25 Sep 2026 16:39:23 +0000 by Dread