目录

主要内容

代码分支

核心代码

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'}}

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

  1. 2.3 spring5源码系列---内置的后置处理器PostProcess加载源码

    本文涉及主题 1. BeanFactoryPostProcessor调用过程源码剖析 2. 配置类的解析过程源码 3. 配置类@Configuration加与不加的区别 4. 重复beanName的覆 ...

  2. Spring源码分析 手写简单IOC容器

    Spring的两大特性就是IOC和AOP. IOC Container,控制反转容器,通过读取配置文件或注解,将对象封装成Bean存入IOC容器待用,程序需要时再从容器中取,实现控制权由程序员向程序的 ...

  3. 框架源码系列六:Spring源码学习之Spring IOC源码学习

    Spring 源码学习过程: 一.搞明白IOC能做什么,是怎么做的  1. 搞明白IOC能做什么? IOC是用为用户创建.管理实例对象的.用户需要实例对象时只需要向IOC容器获取就行了,不用自己去创建 ...

  4. 《四 spring源码》手写springioc框架

    手写SpringIOCXML版本 /** * 手写Spring专题 XML方式注入bean * * * */ public class ClassPathXmlApplicationContext { ...

  5. 利用递归,反射,注解等,手写Spring Ioc和Di 底层(分分钟喷倒面试官)了解一下

    再我们现在项目中Spring框架是目前各大公司必不可少的技术,而大家都知道去怎么使用Spring ,但是有很多人都不知道SpringIoc底层是如何工作的,而一个开发人员知道他的源码,底层工作原理,对 ...

  6. Spring 源码 (2)Spring IOC 容器 前戏准备工作

    Spring 最重要的方法refresh方法 根据上一篇文章 https://www.cnblogs.com/redwinter/p/16141285.html Spring Bean IOC 的创建 ...

  7. 框架源码系列十:Spring AOP(AOP的核心概念回顾、Spring中AOP的用法、Spring AOP 源码学习)

    一.AOP的核心概念回顾 https://docs.spring.io/spring/docs/5.1.3.RELEASE/spring-framework-reference/core.html#a ...

  8. 框架源码系列八:Spring源码学习之Spring核心工作原理(很重要)

    目录:一.搞清楚ApplicationContext实例化Bean的过程二.搞清楚这个过程中涉及的核心类三.搞清楚IOC容器提供的扩展点有哪些,学会扩展四.学会IOC容器这里使用的设计模式五.搞清楚不 ...

  9. Spring 源码 (1)Spring IOC Bean 创建的整体流程

    Spring IOC 中涉及的重要接口 BeanDefinition Bean的描述信息,实现类包括 RootBeanDefinition 和 GenericBeanDefinition,Bean的描 ...

  10. 鸿蒙内核源码分析(进程镜像篇)|ELF是如何被加载运行的? | 百篇博客分析OpenHarmony源码 | v56.01

    百篇博客系列篇.本篇为: v56.xx 鸿蒙内核源码分析(进程映像篇) | ELF是如何被加载运行的? | 51.c.h.o 加载运行相关篇为: v51.xx 鸿蒙内核源码分析(ELF格式篇) | 应 ...

随机推荐

  1. 探索Python @dataclass的内部原理

    之前写过一篇介绍Python中dataclass的文章:<掌握python的dataclass,让你的代码更简洁优雅>. 那篇侧重于介绍dataclass的使用,今天想探索一下这个有趣的特 ...

  2. 平滑升级mariadb

    问题 Centos7自带的MariaDB版本是5.5 ,版本过于老旧,现想升级到最新版本,且数据不丢失 措施 备份原来的数据 mysqldump -u root -p --all-databases ...

  3. unicode编码 asis_2019_unicorn_shop

    这题就是让我们购买第四个商品 当我们输入price为1337.0的时候他会报错,显示要我们只输入一个字符 那么我们就要想怎样用一个字符来表示一个比1337还要大的数字 答案是unicode 编码 (题 ...

  4. 混元API的加密机制与原生集成实战

    今天,我们将重点讨论在对接混元大模型时需要特别关注的几个要点.首先,最为关键的一点是,混元大模型的加密方式相比于其他大模型更为复杂和严密.在对接过程中,我们通常避免使用混元官方提供的SDK进行集成,主 ...

  5. oracle19.3打补丁

    补丁 36582781 - 数据库发布更新 19.24.0.0.240716 本文档在发布时准确无误.有关数据库版本更新 19.24.0.0.240716 的任何更改和其他信息,请参阅 My Orac ...

  6. V-Control 开箱即用的.NET MAUI组件库发布了!

    之前写过挺多的MAUI Sample,其中有很多代码可以打包成组件,当组件完善到一定程度,我会把控件封装起来放到控件库中. 今天,在这个仓库建立一年零八个月后,我觉得可以考虑将其作为开源库发布. 有很 ...

  7. 鸿蒙页面开发 - 组件复用样式 @Styles

    这篇文章介绍一个装饰器 @Styles 他的主要作用是: 当多个组件都有相同的样式,如果每个组件单独设置,会造成大量重复的代码冗余.这时我们可以使用 @Styles 将这些相同样式封装成一个方法,供这 ...

  8. Win10安装MySql步骤

    1.下载 下载地址:https://dev.mysql.com/downloads/mysql/ 文件地址:https://dev.mysql.com/get/Downloads/MySQL-8.3/ ...

  9. Kali Linux 简介

    Kali Linux 简介 Kali Linux 是一个由 Offensive Security 公司开发.维护和资助的基于 Debian 的 Linux 发行版,专为高级渗透测试和安全审计而设计.它 ...

  10. Zabbix Proxy安装及替换Zabbix阿里云源脚本

    zabbix proxy安装步骤 说明: Zabbix Proxy使用的是独立的数据库实例,如果放在一起数据容易遭到破坏;Proxy仅仅是一个数据采集的作用,其他的依然是依靠Server端实现,这就会 ...