[源码系列:手写spring] IOC第七节:加载xml文件中定义的Bean
目录
主要内容
上一节加入了资源管理器,令我们的框架具备了读取配置文件的能力。在该篇文章中将为我们的框架加入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的更多相关文章
- 2.3 spring5源码系列---内置的后置处理器PostProcess加载源码
本文涉及主题 1. BeanFactoryPostProcessor调用过程源码剖析 2. 配置类的解析过程源码 3. 配置类@Configuration加与不加的区别 4. 重复beanName的覆 ...
- Spring源码分析 手写简单IOC容器
Spring的两大特性就是IOC和AOP. IOC Container,控制反转容器,通过读取配置文件或注解,将对象封装成Bean存入IOC容器待用,程序需要时再从容器中取,实现控制权由程序员向程序的 ...
- 框架源码系列六:Spring源码学习之Spring IOC源码学习
Spring 源码学习过程: 一.搞明白IOC能做什么,是怎么做的 1. 搞明白IOC能做什么? IOC是用为用户创建.管理实例对象的.用户需要实例对象时只需要向IOC容器获取就行了,不用自己去创建 ...
- 《四 spring源码》手写springioc框架
手写SpringIOCXML版本 /** * 手写Spring专题 XML方式注入bean * * * */ public class ClassPathXmlApplicationContext { ...
- 利用递归,反射,注解等,手写Spring Ioc和Di 底层(分分钟喷倒面试官)了解一下
再我们现在项目中Spring框架是目前各大公司必不可少的技术,而大家都知道去怎么使用Spring ,但是有很多人都不知道SpringIoc底层是如何工作的,而一个开发人员知道他的源码,底层工作原理,对 ...
- Spring 源码 (2)Spring IOC 容器 前戏准备工作
Spring 最重要的方法refresh方法 根据上一篇文章 https://www.cnblogs.com/redwinter/p/16141285.html Spring Bean IOC 的创建 ...
- 框架源码系列十:Spring AOP(AOP的核心概念回顾、Spring中AOP的用法、Spring AOP 源码学习)
一.AOP的核心概念回顾 https://docs.spring.io/spring/docs/5.1.3.RELEASE/spring-framework-reference/core.html#a ...
- 框架源码系列八:Spring源码学习之Spring核心工作原理(很重要)
目录:一.搞清楚ApplicationContext实例化Bean的过程二.搞清楚这个过程中涉及的核心类三.搞清楚IOC容器提供的扩展点有哪些,学会扩展四.学会IOC容器这里使用的设计模式五.搞清楚不 ...
- Spring 源码 (1)Spring IOC Bean 创建的整体流程
Spring IOC 中涉及的重要接口 BeanDefinition Bean的描述信息,实现类包括 RootBeanDefinition 和 GenericBeanDefinition,Bean的描 ...
- 鸿蒙内核源码分析(进程镜像篇)|ELF是如何被加载运行的? | 百篇博客分析OpenHarmony源码 | v56.01
百篇博客系列篇.本篇为: v56.xx 鸿蒙内核源码分析(进程映像篇) | ELF是如何被加载运行的? | 51.c.h.o 加载运行相关篇为: v51.xx 鸿蒙内核源码分析(ELF格式篇) | 应 ...
随机推荐
- 探索Python @dataclass的内部原理
之前写过一篇介绍Python中dataclass的文章:<掌握python的dataclass,让你的代码更简洁优雅>. 那篇侧重于介绍dataclass的使用,今天想探索一下这个有趣的特 ...
- 平滑升级mariadb
问题 Centos7自带的MariaDB版本是5.5 ,版本过于老旧,现想升级到最新版本,且数据不丢失 措施 备份原来的数据 mysqldump -u root -p --all-databases ...
- unicode编码 asis_2019_unicorn_shop
这题就是让我们购买第四个商品 当我们输入price为1337.0的时候他会报错,显示要我们只输入一个字符 那么我们就要想怎样用一个字符来表示一个比1337还要大的数字 答案是unicode 编码 (题 ...
- 混元API的加密机制与原生集成实战
今天,我们将重点讨论在对接混元大模型时需要特别关注的几个要点.首先,最为关键的一点是,混元大模型的加密方式相比于其他大模型更为复杂和严密.在对接过程中,我们通常避免使用混元官方提供的SDK进行集成,主 ...
- oracle19.3打补丁
补丁 36582781 - 数据库发布更新 19.24.0.0.240716 本文档在发布时准确无误.有关数据库版本更新 19.24.0.0.240716 的任何更改和其他信息,请参阅 My Orac ...
- V-Control 开箱即用的.NET MAUI组件库发布了!
之前写过挺多的MAUI Sample,其中有很多代码可以打包成组件,当组件完善到一定程度,我会把控件封装起来放到控件库中. 今天,在这个仓库建立一年零八个月后,我觉得可以考虑将其作为开源库发布. 有很 ...
- 鸿蒙页面开发 - 组件复用样式 @Styles
这篇文章介绍一个装饰器 @Styles 他的主要作用是: 当多个组件都有相同的样式,如果每个组件单独设置,会造成大量重复的代码冗余.这时我们可以使用 @Styles 将这些相同样式封装成一个方法,供这 ...
- Win10安装MySql步骤
1.下载 下载地址:https://dev.mysql.com/downloads/mysql/ 文件地址:https://dev.mysql.com/get/Downloads/MySQL-8.3/ ...
- Kali Linux 简介
Kali Linux 简介 Kali Linux 是一个由 Offensive Security 公司开发.维护和资助的基于 Debian 的 Linux 发行版,专为高级渗透测试和安全审计而设计.它 ...
- Zabbix Proxy安装及替换Zabbix阿里云源脚本
zabbix proxy安装步骤 说明: Zabbix Proxy使用的是独立的数据库实例,如果放在一起数据容易遭到破坏;Proxy仅仅是一个数据采集的作用,其他的依然是依靠Server端实现,这就会 ...