[源码系列:手写spring] IOC第七节:加载xml文件中定义的Bean

目录

主要内容

代码分支

核心代码

BeanDefinitionReader

AbstractBeanDefinitionReader

XmlBeanDefinitionReader

测试

bean定义文件spring.xml

Java代码

测试结果


主要内容

上一节加入了资源管理器,令我们的框架具备了读取配置文件的能力。在该篇文章中将为我们的框架加入xml格式的配置文件,在配置文件中声明式的定义bean信息,利用资源加载器读取xml配置文件,进而解析出bean信息,注入进bean容器。

BeanDefinitionReader:读取bean定义信息的接口,获取资源后,读取bean定义信息,生成BeanDefinition注册入容器。

AbstractBeanDefinitionReader:BeanDefinitionReader的抽象实现类,从需要实现的抽象类BeanDefinitionReader功能上看,该类需要拥有 ResourceLoader 和 BeanDefinitionRegistry 两个属性。

XmlBeanDefinitionReader:实现从xml中读取和加载Bean定义信息。

由于xml文件中读取的都是文本,本章节bean属性暂时只支持String类型和引用其他bean,后面会讲解类型转换器,实现类型转换。

注意:本章对BeanFactory的体系做了调整。

代码分支

除去添加的核心代码外,其他地方也有非常细微的改动,想要测试代码可行性的同学可以查看该分支下具体代码。

xml-file-define-bean 加载XML资源中的Bean剖析Spring源码,包括常见特性IOC、AOP、三级缓存等... Contribute to yihuiaa/little-spring development by creating an account on GitHub.https://github.com/yihuiaa/little-spring/tree/xml-file-define-bean

核心代码

BeanDefinitionReader

定义加载资源并且读取Bean定义信息的接口方法,包括方法有:获取Bean注册表、获取资源加载器、从静态资源读取BeanDefinition,根据路径加载BeanDefinition。

Reader中加载的含义:reader的加载目标是资源下的Bean,不仅是read资源,还包括load Bean,registe Bean,即读取并注册,利用ResourceLoader加载资源后,读取Bean定义信息并注册到注册表。

public interface BeanDefinitionReader {
    /**
     * 获取注册表
     * @return
     */
    BeanDefinitionRegistry getRegistry();

    /**
     * 获取资源加载器
     * @return
     */
    ResourceLoader getResourceLoader();

    void loadBeanDefinitions(Resource resource) throws BeansException;

    void loadBeanDefinitions(String location) throws BeansException;

    void loadBeanDefinitions(String[] locations) throws BeansException;
}

AbstractBeanDefinitionReader

BeanDefinitionReader的抽象实现类

public abstract class AbstractBeanDefinitionReader implements BeanDefinitionReader{

    private final BeanDefinitionRegistry registry;
    private ResourceLoader resourceLoader;

    protected AbstractBeanDefinitionReader(BeanDefinitionRegistry registry) {
        this(registry, new DefaultResourceLoader());
    }

    @Override public BeanDefinitionRegistry getRegistry() {
        return this.registry;
    }

    public AbstractBeanDefinitionReader(BeanDefinitionRegistry registry, ResourceLoader resourceLoader) {
        this.registry = registry;
        this.resourceLoader = resourceLoader;
    }

    @Override public ResourceLoader getResourceLoader() {
        return resourceLoader;
    }

    @Override public void loadBeanDefinitions(String[] locations) throws BeansException {
        for (String location : locations) {
            loadBeanDefinitions(location);
        }
    }
}

XmlBeanDefinitionReader

定义了一些字符常量表示xml节点名称,实现两个loadBeanDefinitions(),实现xml中BeanDefinition的读取和加载

package org.springframework.beans.factory.xml;

import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.XmlUtil;
import org.springframework.beans.BeansException;
import org.springframework.beans.PropertyValue;
import org.springframework.beans.core.io.Resource;
import org.springframework.beans.core.io.ResourceLoader;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanReference;
import org.springframework.beans.factory.support.AbstractBeanDefinitionReader;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

import java.io.IOException;
import java.io.InputStream;

/**
 * ● @author: YiHui
 * ● @date: Created in 17:41  2023/4/9
 */
public class XmlBeanDefinitionReader  extends AbstractBeanDefinitionReader {

    public static final String BEAN_ELEMENT = "bean";
    public static final String PROPERTY_ELEMENT = "property";
    public static final String ID_ATTRIBUTE = "id";
    public static final String NAME_ATTRIBUTE = "name";
    public static final String CLASS_ATTRIBUTE = "class";
    public static final String VALUE_ATTRIBUTE = "value";
    public static final String REF_ATTRIBUTE = "ref";
    public XmlBeanDefinitionReader(BeanDefinitionRegistry registry) {
        super(registry);
    }

    public XmlBeanDefinitionReader(BeanDefinitionRegistry registry, ResourceLoader resourceLoader) {
        super(registry, resourceLoader);
    }
    @Override public void loadBeanDefinitions(String location) throws BeansException {
        ResourceLoader resourceLoader = getResourceLoader();
        Resource resource = resourceLoader.getResource(location);
        this.loadBeanDefinitions(resource);
    }
    @Override public void loadBeanDefinitions(Resource resource) throws BeansException {
        try {
            InputStream inputStream = resource.getInputStream();
            doLoadBeanDefinitions(inputStream);
        }catch (IOException e) {
            throw new BeansException("IOException parsing XML document from " + resource, e);
        }
    }
    private void doLoadBeanDefinitions(InputStream inputStream){
        Document document = XmlUtil.readXML(inputStream);
        Element rootElement = document.getDocumentElement();
        NodeList childNodes = rootElement.getChildNodes();
        for (int i = 0; i < childNodes.getLength();i++) {
            if(childNodes.item(i) instanceof Element){
                if(BEAN_ELEMENT.equals(childNodes.item(i).getNodeName())){
                    //解析Bean标签
                    Element bean = (Element) childNodes.item(i);
                    String id = bean.getAttribute(ID_ATTRIBUTE);
                    String name = bean.getAttribute(NAME_ATTRIBUTE);
                    String className = bean.getAttribute(CLASS_ATTRIBUTE);

                    Class<?> clazz = null;
                    try {
                        clazz = Class.forName(className);
                    } catch (ClassNotFoundException e) {
                        throw new BeansException("Cannot find class [" + className + "]");
                    }


                    //id优先于name
                    String beanName = StrUtil.isNotEmpty(id) ? id : name;
                    if (StrUtil.isEmpty(beanName)) {
                        //如果id和name都为空,将类名的第一个字母转为小写后作为bean的名称
                        beanName = StrUtil.lowerFirst(clazz.getSimpleName());
                    }

                    BeanDefinition beanDefinition = new BeanDefinition(clazz);

                    //Bean属性
                    for(int j = 0; j < bean.getChildNodes().getLength(); j++) {
                        if(PROPERTY_ELEMENT.equals(bean.getChildNodes().item(j).getNodeName())){
                            //解析property标签
                            Element property = (Element) bean.getChildNodes().item(j);
                            String nameAttribute = property.getAttribute(NAME_ATTRIBUTE);
                            String valueAttribute = property.getAttribute(VALUE_ATTRIBUTE);
                            String refAttribute = property.getAttribute(REF_ATTRIBUTE);

                            if (StrUtil.isEmpty(nameAttribute)) {
                                throw new BeansException("The name attribute cannot be null or empty");
                            }

                            Object value = valueAttribute;
                            if (StrUtil.isNotEmpty(refAttribute)) {
                                value = new BeanReference(refAttribute);
                            }

                            PropertyValue propertyValue = new PropertyValue(nameAttribute,value);
                            beanDefinition.getPropertyValues().addPropertyValue(propertyValue);
                        }
                    }

                    if (getRegistry().containsBeanDefinition(beanName)) {
                        //beanName不能重名
                        throw new BeansException("Duplicate beanName[" + beanName + "] is not allowed");
                    }
                    //注册BeanDefinition
                    getRegistry().registerBeanDefinition(beanName, beanDefinition);
                }           }
        }

    }
}

测试

bean定义文件spring.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
	         http://www.springframework.org/schema/beans/spring-beans.xsd
		 http://www.springframework.org/schema/context
		 http://www.springframework.org/schema/context/spring-context-4.0.xsd">

    <bean id="person" class="bean.Person">
        <property name="name" value="yiHui"/>
        <property name="car" ref="car"/>
    </bean>

    <bean id="car" class="bean.Car">
        <property name="name" value="Rolls-Royce"/>
    </bean>

</beans>

Java代码

	@Test
	public void testXmlFile() throws Exception {
		DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
		XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
		beanDefinitionReader.loadBeanDefinitions("classpath:spring.xml");

		Person person = (Person) beanFactory.getBean("person");
		System.out.println(person);
		assertThat(person.getName()).isEqualTo("derek");
		assertThat(person.getCar().getBrand()).isEqualTo("porsche");

		Car car = (Car) beanFactory.getBean("car");
		System.out.println(car);
		assertThat(car.getBrand()).isEqualTo("porsche");
	}
}

测试结果

Person{name='yiHui', age='null', car=Car{name='Rolls-Royce'}}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值