目录

主要内容

代码分支

核心代码

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. VSTO踩坑记录(3)- 用ClickOnce发布outlook插件 - 我数据文件呢?

    概述 上面两篇文章虽然很简略,不过vsto说白了就是winform开发,所以需要说的内容不是很多,下面直接到发布阶段. 这次先介绍ClickOnce的发布方式,默认右击项目 - 发布,就是这种方式. ...

  2. SpringCloud (五) - 云服务器Centos7.6,安装JDK,Maven,Mysql,Redis

    1.购买云服务器 购买地址:https://cloud.tencent.com/act/pro/2022double11_warmup 后面的环境都是基于此环境Centos7.6: 2.安装 Secu ...

  3. 分布式事务-XA-DTP-TCC-BASE介绍

    前言 数据库事务(简称:事务,Transaction)是指数据库执行过程中的一个逻辑单位,由一个有限的数据库操作序列构成. 事务拥有以下四个特性,习惯上被称为 ACID 特性: 原子性(Atomici ...

  4. Golang-并发9

    http://c.biancheng.net/golang/concurrent/ Go语言并发简述(并发的优势) Go语言的并发机制运用起来非常简便,在启动并发的方式上直接添加了语言级的关键字就可以 ...

  5. Tomcat启用manager管理

    1.修改远程访问限制: 修改webapps/manager/META-INF/context.xml和webapps/host-manager/META-INF/context.xml,在<Co ...

  6. 微软宣布更新SymCrypt加密库,新增对PQC算法的支持

    转载链接:https://mp.weixin.qq.com/s/aWXzPTWhxFpJVP1s0iwAtw 2024年9月9日,微软(Microsoft)在其博客中宣布,已开始在其开源核心加密库Sy ...

  7. Confusion pg walkthrough Intermediate

    namp ┌──(root㉿kali)-[~] └─# nmap -p- -A 192.168.188.99 Starting Nmap 7.94SVN ( https://nmap.org ) at ...

  8. oracle和sqlserver对于事务数据库死锁处理的区别

    create table aa_test ( id int constraint TEST_PK primary key, name varchar2(50) ) / insert into aa_t ...

  9. Luogu P3059 Concurrently Balanced Strings G 题解 [ 紫 ] [ 线性 dp ] [ 哈希 ] [ 括号序列 ]

    模拟赛搬的题,dp 思路很明显,但难点就在于找到要转移的点在哪. 暴力 首先我们可以先考虑 \(k=1\) 的情况,这应该很好想,就是对于每一个右括号,找到其匹配的左括号,然后进行转移即可,这个过程可 ...

  10. [TJOI2019] 甲苯先生的字符串 题解

    有点水了-- 考虑相邻的不能放在一起,不相邻的可以,那么很容易想到转移方程: \[dp_{i,j}=\sum_{k=0}^{25}dp_{i-1,k}[j,k不相邻] \] 其中 \(dp_{i,j} ...