《Spring源码深度解析》二
第二章:容器的基本实现
2.1 基本用法
首先定义一个Bean:(假设在bean包下)

然后定义配置文件:

测试类:

当然,这并非是企业级用法,此处只是用来分析学习其实现
2.2 功能分析
上述三图代码功能如下:
1.读取配置文件beanFactocyTest.xml
2.根据beanFactocyTest.xml中的配置找到对应的类的配置,并实例化
3.调用实例化后的对象
可以猜测,总共需要三个类:ConfigReader用来读取配置文件,ReflectionUtil用来反射实例化配置中的bean,App用于完成整个逻辑的串联
架构图:

下面就来看看Sping的实现
2.3 org.springframework.beans初步介绍
2.3.1 工程目录

2.3.2 两个核心类
2.3.2.1 DefaultListableBeanFactory
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable
整个bean加载的核心部分,是Spring注册及加载bean的默认实现
XmlBeanFactory继承此类,使用了自定义的XML读取器XmlBeanDefinitionReader,实现了个性化的BeanDefinitionReader读取,主要使用reader属性对资源文件进行读取和注册,而注册及获取bean都是使用DefaultListableBeanFactory的方法
类结构图:idea中F4可打开

容器加载相关类图及其作用:

2.3.2.2 XmlBeanDefinitionReader
即2.3.2.1中解释的XmlBeanFactory使用的自定义XML读取器,实现对资源文件的读取、解析及注册,即Resource相关类完成了对配置文件的封装后,它会完成配置文件的读取工作
配置文件读取相关类图及其作用:

XmlBeanDefinitionReader处理流程:
1.通过继承的AbstractBeanDefinitionReader的方法,使用ResourLoader将资源文件路径转换为对应的Resouce文件
2.通过DocumentLoader将Resouce文件转换为Document文件
3.通过DefaultBeanDefinitionDocumentReader对Document进行解析,并使用BeanDefinitionParserDelegate进行解析
2.4 2.1中代码的功能实现
2.4.1 BeanFactory bf = new XmlBeanFactory(new ClassPathResource("beanFactoryTest.xml"));
处理时序图:

流程:首先创建和封装Resource对象,传给XmlBeanFactory后,调用loadBeanDefinitions,最终返回BeanFactory对象。
那么,如何封装Resource资源呢?
2.4.1.1 封装Resource资源
ClassPathResource:封装配置文件,使用Resource接口封装底层资源,如File、URL、Classpath等
底层资源注:不同来源的资源为URL,通过注册不同的handler(如URLStreamHandler)来处理不同来源的读取逻辑;handler使用前缀+协议(Protocol)属性来识别不同来源,如"file:""http:""jar:"等
Resource相关接口:

不同来源的资源对应的Resource实现:

getInputStream方法:
从实现类的类名可以推出,实现类组合了其类名的类,如下两个例子:
ClassPathResource:通过class或classLoader提供的底层方法进行调用,组合了path

FileSystemResource:使用FileInputStream对文件进行实例化,组合了file

技巧:日常开发中,可以直接使用Spring提供的类来加载资源,如下图:

2.4.1.2 流程的其他步骤实现
XmlBeanFactory初始化,使用Resourse示例作为构造方法参数:

首先会调用其间接父类AbstractAutowireCapableBeanFactory的构造方法:

其ignoreDependencyInterface方法主要功能是忽略给定接口的自动装配功能,为什么要有这个方法呢?

接着回到XmlBeanFactory构造方法的loadBeanDefinitions(resource),这个方法是整个资源加载的切入点
时序图:

其处理过程如下:(其中2.通过SAX读取XML文件来准备InputSource)

EncodedResource封装了有编码的InputStreamReader
构造好EncodedResource后,就进入了真正的数据准备阶段loadBeanDefinitions,即"处理时序图"所示的第三阶段
这个方法的逻辑核心部分为doLoadBeanDefinitions,做了下列三件事情:

这三件事情,我们来逐步分析
1.获取对XML文件的验证模式:getValidationModeForResource (Resource resource)
a 常用的验证模式:DTD、XSD
可以通过比较XML文档和DTD 文件来看文档是否符合规范,元素和标签使用是否正确
也可以用一个指定的XML Schema(本身也是XML)来验证某个XML 文档, 以检查该XML文档是否符合其要求
b 验证模式的读取,可以通过setValidationMode进行设定验证模式
流程代码:

detectValidationMode方法使用委派模式,委托给了XmlValiadationModeDetector(通过组合此类)的validationModeDetector方法来实现自动检测验证模式:
判断是否包含DOCTYPE ,如果包含就是DTD ,否则就是XSD
2.加载XML文件,并得到对应的Document:loadDocument
同样地,XmlBeanFactoryReader将文档读取任务委托给了DocumentLoader接口,真正调用的实现类为DefaultDocumentLoader,通过SAX来解析XML文档(相同的套路,首先创建DocumentBuilderFactory ,再通
过DocumentBuilderFactory创建DocumentBuilder, 进而解析inputSource来返回Document 对象。)
loadDocument方法中涉及一个参数EntityResolver,下面解释一下
2.1 EntityResolver
可以提供一个如何寻找DTD声明的方法,即由程序来实现寻找DTD声明的过程,比如我们将DTD文件放到项目中某处,在实现时直接将此文档读取并返回给SAX 即可。这样就避免了通过网络来寻找相应的声明,加快速度。
2.1.1 通过getEntityResolver方法获取

2.1.2 如何将URL转换为工程对应的地址文件呢?
对不同的验证模式,Spring 使用了不同的解析器解析
比如加载DTD类型的BeansDtdResolver的resolveEntity是直接截取systemld 最后的xx.dtd
然后去当前路径下寻找,而加载XSD类型的PluggableSchemaResolver 类的resolveEntity 是默
认到META-INF/Spring.schemas 文件巾找到systemid 所对应的XSD 文件并加载。
3.根据返回的Document注册Bean信息
拥有Documemt对象时,调用下面方法
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
//实例化
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
//记录统计前BeanDefinition的加载个数
int countBefore = getRegistry().getBeanDefinitionCount();
//加载注册bean
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
//记录本次加载的BeanDefinition个数
return getRegistry().getBeanDefinitionCount() - countBefore;
}
@Override
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
doRegisterBeanDefinitions(doc.getDocumentElement());
}
DefaultBeanDefinitionDocumentReader的doRegisterBeanDefinitions(doc.getDocumentElement());真正地开始解析:
protected void doRegisterBeanDefinitions(Element root) {
//专门处理解析
BeanDefinitionParserDelegate parent = this.delegate;
this.delegate = createDelegate(getReaderContext(), root, parent);
if (this.delegate.isDefaultNamespace(root)) {
//处理profile属性
String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
if (StringUtils.hasText(profileSpec)) {
String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
if (logger.isDebugEnabled()) {
logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec +
"] not matching: " + getReaderContext().getResource());
}
return;
}
}
}
//解析前处理,留给子类实现
preProcessXml(root);
//解析
parseBeanDefinitions(root, this.delegate);
//解析后处理,留给子类实现
postProcessXml(root);
this.delegate = parent;
}
涉及模板方法模式,DefaultBeanDefinitionDocumentReader的子类如果需要在Bean解析前后做一些处理的话,那么只需要重写preProcessXml(root)和postProcessXml(root))
而parseBeanDefinitions则对不同类Bean分别做不同的处理
/**
* 处理两大类Bean声明
* 一个是默认的,如:<bean id=”test” class=”test.TestBean” />
* 另一类就是自定义的,如:<tx :annotation-driven/>
* @param root
* @param delegate
*/
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
if (delegate.isDefaultNamespace(root)) {
NodeList nl = root.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element) {
Element ele = (Element) node;
if (delegate.isDefaultNamespace(ele)) {
//默认命名空间
parseDefaultElement(ele, delegate);
}
else {
//自定义命名空间
delegate.parseCustomElement(ele);
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}
总结
DefaultListableBeanFactory和XmlBeanDefinitionReader是Spring容器基本实现的两个核心类
DefaultListableBeanFactory是整个bean加载的核心部分,是Spring注册及加载bean的默认实现
XmlBeanDefinitionReader则是XmlBeanFactory使用的自定义XML读取器,实现对资源文件的读取、解析及注册
创建BeanFactory的流程为:首先创建和封装Resource对象,传给XmlBeanFactory后,调用loadBeanDefinitions,最终返回BeanFactory对象。
《Spring源码深度解析》二的更多相关文章
- PHP面向对象编程 对象的基本概念 PHP面向对象的基本实践 PHP面向对象的高级实践 PHP面向对象的特殊实践
再次梳理一下面向对象编程的要点. 此文是以php为例,但思想是通用的. 总结的PHP面向对象编程笔记 对象的基本概念 对象的基本构成 对象包含两部分 一.对象的组成元素 是对象的数据模型,用于描述对象 ...
- 【Scrum】-NO.40.EBook.1.Scrum.1.001-【敏捷软件开发:原则、模式与实践】- Scrum
1.0.0 Summary Tittle:[Scrum]-NO.40.EBook.1.Scrum.1.001-[敏捷软件开发:原则.模式与实践]- Scrum Style:DesignPattern ...
- (转) 面向对象设计原则(二):开放-封闭原则(OCP)
原文:https://blog.csdn.net/tjiyu/article/details/57079927 面向对象设计原则(二):开放-封闭原则(OCP) 开放-封闭原则(Open-closed ...
- 敏捷软件开发:原则、模式与实践——第11章 DIP:依赖倒置原则
第11章 DIP:依赖倒置原则 DIP:依赖倒置原则: a.高层模块不应该依赖于低层模块.二者都应该依赖于抽象. b.抽象不应该依赖于细节.细节应该依赖于抽象. 11.1 层次化 下图展示了一个简单的 ...
- 敏捷软件开发:原则、模式与实践——第9章 OCP:开放-封闭原则
第9章 OCP:开放-封闭原则 软件实体(类.模块.函数等)应该是可以扩展的,但是不可修改. 9.1 OCP概述 遵循开放-封闭原则设计出的模块具有两个主要特征: (1)对于扩展是开放的(open f ...
- OOP 面向对象 七大原则 (二)
OOP 面向对象 七大原则 (二) 上一篇写到了前四个原则,这一篇继续~~ 接口隔离:客户端不应该依赖它不需要的接口:一个类对另一个类的依赖应该建立在最小的接口上. 又是一句大白话~就是说接口尽量 ...
- 手机淘宝轻店业务 Serverless 研发模式升级实践
一.前言 随着 Serverless 在业界各云平台落地,阿里内部 Serverless 研发平台.各种研发模式也在业务中逐步落地,如火如荼.在此契机下,淘系团队启动了轻店 Serverless 研发 ...
- ASP.NET MVC5 网站开发实践(二) Member区域–管理列表、回复及删除
本来想接着上次把这篇写完的,没想到后来工作的一些事落下了,放假了赶紧补上. 目录: ASP.NET MVC5 网站开发实践 - 概述 ASP.NET MVC5 网站开发实践(一) - 项目框架 ASP ...
- ASP.NET MVC5 网站开发实践(二) Member区域–我的咨询列表及添加咨询
上次把咨询的架构搭好了,现在分两次来完成咨询:1.用户部分,2管理部分.这次实现用户部分,包含两个功能,查看我的咨询和进行咨询. 目录: ASP.NET MVC5 网站开发实践 - 概述 ASP.NE ...
- ASP.NET MVC5 网站开发实践(二) Member区域 - 咨询管理的架构
咨询.留言.投诉等功能是网站应具备的基本功能,可以加强管理员与用户的交流,在上次完成文章部分后,这次开始做Member区域的咨询功能(留言.投诉都是咨询).咨询跟文章非常相似,而且内容更少.更简单. ...
随机推荐
- 借助marquee实现弹幕效果
HTML标签marquee实现滚动效果 .基于此,实现简易版 弹幕: HTML <div class="right_liuyan"> <marquee id=& ...
- Egret入门学习日记 --- 第五篇(书中 3.5节 内容)
第五篇(书中 3.5节 内容) 今天得把昨天的问题解决了才行. 去了Q群,碰到一位大大,他给我解惑了.Thanks♪(・ω・)ノ 这是我之前按照书上写的方式写的,并没有效果. 然后大大给我解答了: 后 ...
- python基础-并发编程之I/O模型基础
1. I/O模型介绍 1.1 I/O模型基础 更好的理解I/O模型,需要先回顾:同步.异步.阻塞.非阻塞 同步:执行完代码后,原地等待,直至出现结果 异步:执行完代码后,不等待,继续执行其他事务(常与 ...
- 【VS开发】【电子电路技术】RJ45以太网传输线研究
RJ45以太网传输线研究 最近研究远距离差分视频传输方案,理所当然想到了LVDS协议.至于选用cameralink传输线,还是选用其他方案,本人更倾向于廉价的RJ45以太网线来实现LVDS差分信号的传 ...
- Linux 释放占用端口
在使用 Swoole 扩展库的 WebSocket 服务时,直接退出终端,但是 WebSocket 使用的端口依然坚挺的在被占用着. 导致再次启动 WebSocket 服务失败. 具体操作: 1.ne ...
- poj2186--tarjan+缩点(有向图的强连通分量中点的个数)
题目大意: 每一头牛的愿望就是变成一头最受欢迎的牛.现在有N头牛,给你M对整数(A,B),表示牛A认为牛B受欢迎. 这 种关系是具有传递性的,如果A认为B受欢迎,B认为C受欢迎,那么牛A也 ...
- Yii中实例化类的四种方式
1.最简单的也是最基本的,大家都会的一种 use yii\caching\FileCache; $cache = new FileCache(); $cache->set("name& ...
- 现身说法:面对DDoS攻击时该如何防御?
上周,我们的网站遭到了一次DDoS攻击.虽然我对DDoS的防御还是比较了解,但是真正遇到时依然打了我个措手不及.DDoS防御是一件比较繁琐的事,面对各种不同类型的攻击,防御方式也不尽相同.对于攻击来的 ...
- Maven项目构建利器04——Maven的一些核心概念
1.坐标 1)数学上的坐标: [1].在平面上. 使用X,Y两个向量可以唯一的定位平面上的任意一个点 [2]在空间中, 使用X,Y,Z三个向量可以唯一的定位空间中的任何一个点 2)Maven中的坐标: ...
- SIP协议 会话发起协议(二)
SIP - 响应码 SIP响应是由用户代理服务器(UAS)或SIP服务器生成的用于回复客户端生成的请求的消息.这可能是一个正式的确认,以防止UAC转发请求. 响应可能包含UAC所需的一些额外的信息头字 ...