Spring源码学习-容器BeanFactory(一) BeanDefinition的创建-解析资源文件
写在前面
从大四实习至今已一年有余,作为一个程序员,一直没有用心去记录自己工作中遇到的问题,甚是惭愧,打算从今日起开始养成写博客的习惯。作为一名java开发人员,Spring是永远绕不过的话题,它的设计精巧,代码优美,值得每一名开发人员学习阅读。
在我最开始学习javaEE时,第一次接触Spring是从一个S(Struts)S(Spring)H(Herbinate)的框架开始。由java原生开发到框架开发转换过程中,那时我的印象里Struts负责控制层,herbinate负责数据层,而Spring则是业务层。他有两个核心特点:注入对象(代码原生中的工厂类)和事务控制(当时对切面编程的狭隘理解)。
实际上Spring给我们提供的最重要的两个特性就是如此,控制反转(IOC)和切面编程(AOP)。Spring到底是如何实现这两个特性的?这个问题是我们阅读Spring源码时仔细思考的问题,一切从Spring的源头,容器的加载开始。本人工作时间较短,经验浅薄。在学习源码的过程中主要是跟随书籍《Spring源码深度解析》(郝佳著)的脚步一点一点阅读,从去年至今已经断断续续的阅读了一部分Spring源码,打算慢慢把自己的心得和看法记录下来,一作为读书笔记方便日后的查阅复习,如果能给他人带来一点帮助,欣喜至极。所有的源码皆浅尝而止,我选择我认为比较重要的部分记录下来,如果想更深一步学习,可以自己阅读或者阅读上述书籍《Spring源码深度解析》。
一.Spring源码学习-容器BeanFactory和元素SpringBean的前世今生
1.读取并解析资源文件
1.1 BeanDefinition的创建 - 解析资源文件
Resource resource = new ClassPathResource("beanFactory.xml");
BeanFactory beanFactory = new XmlBeanFactory(resource);
Student student = beanFactory.getBean(Student.class);
上面是读取一个xml文件创建一个最基础的Spring容器,并且从容器中获取一个Bean元素。在整个过程中我将其主要分为二个阶段:
- 读取资源文件,将配置信息转化为BeanDefinition缓存起来,以备后续创建实例对象。
- 根据创建信息,创建Bean并且完成依赖注入的过程。
Resource接口
首先,我们看一下Resouce这个接口的定义

不管是xml、properties、yml,Spring对外在的所有资源文件创建了统一接口Resource,这是我阅读Spring源码体会OOP编程优美之处的起始点。
Spring核心包中自身集成了很多Resource实现类,除了此处我们使用了ClassPathResource,还有常见的URLResource和FileSystemResource等等。如果我们项目中有需要定位的资源文件,也可以考虑使用上面几个类,通过InputStream inputStream = resource.getInputStream();方法获取输入流后进行我们自己的处理。
跟随XmlBeanFactory的构造函数,我们正式开始探寻Spring容器的奥秘。
private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);
public XmlBeanFactory(Resource resource) throws BeansException {
this(resource, null);
}
public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
super(parentBeanFactory);
this.reader.loadBeanDefinitions(resource);
}
在构造函数互相调用的过程中,我们看到XmlBeanFactory将资源文件交给了内部的XmlBeanDefinitionReader来进行资源文件的解析。loadBeanDefinitions(...)这个方法我们往上追溯的话,就能看到它是在BeanDefinitionReader接口中定义的。

看到这个类的名称就能猜到既然有XML,那就应该还可以有其他各种文件对应的实现类,如果我们需要我们甚至可以基于JS实现一个JsBeanDefinitionReader。自己写一个简单的Spring容器,实现基础的IOC功能一直是我自我提升计划之一,后续应该会找时间去动手实现。
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
return loadBeanDefinitions(new EncodedResource(resource));
}
XmlBeanDefinitionReader在拿到Resource资源后首先将资源文件进行再次编码,然后交给了同类中的命名重载方法。
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
Assert.notNull(encodedResource, "EncodedResource must not be null");
if (logger.isInfoEnabled()) {
logger.info("Loading XML bean definitions from " + encodedResource.getResource());
}
//todo flash 不太理解为什么要把当前资源文件放入类中ThreadLocal的Set集合中
Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
if (currentResources == null) {
currentResources = new HashSet<EncodedResource>(4);
this.resourcesCurrentlyBeingLoaded.set(currentResources);
}
if (!currentResources.add(encodedResource)) {
throw new BeanDefinitionStoreException(
"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
}
try {
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
//获取资源流,创建XML的资源流
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
//核心逻辑方法
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
finally {
inputStream.close();
}
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"IOException parsing XML document from " + encodedResource.getResource(), ex);
}
finally {
currentResources.remove(encodedResource);
if (currentResources.isEmpty()) {
this.resourcesCurrentlyBeingLoaded.remove();
}
}
}
上面的方法主要是数据文件的准备阶段(但目前不太理解的是为什么要把数据文件放入类中ThreadLocal的一个Set集合中,且处理完毕时还进行了移除操作),核心逻辑交给了同类中的doLoadBeanDefinitions。
在Spring的编码风格中通常一个操作非核心的数据装饰、验证,后续收尾操作在一个方法中,而核心操作往往在装饰方法前方加do,此处即是如此。
Spring代码庞大,接口众多。源码在阅读的过程中在各个接口和实现类中来回跳转,很容易就迷失方向。所以在阅读时,我更愿意以少量多次的方式去进行,搞清楚一个阶段,再继续往下,所以对应的阅读笔记也会以这样的形式去书写吧。下一篇,再见。
Spring源码学习-容器BeanFactory(一) BeanDefinition的创建-解析资源文件的更多相关文章
- Spring源码学习-容器BeanFactory(二) BeanDefinition的创建-解析前BeanDefinition的前置操作
写在前面 上文 Spring源码学习-容器BeanFactory(一) BeanDefinition的创建-解析资源文件主要讲Spring容器创建时通过XmlBeanDefinitionReader读 ...
- Spring源码学习-容器BeanFactory(三) BeanDefinition的创建-解析Spring的默认标签
写在前面 上文Spring源码学习-容器BeanFactory(二) BeanDefinition的创建-解析前BeanDefinition的前置操作中Spring对XML解析后创建了对应的Docum ...
- Spring源码学习-容器BeanFactory(四) BeanDefinition的创建-自定义标签的解析.md
写在前面 上文Spring源码学习-容器BeanFactory(三) BeanDefinition的创建-解析Spring的默认标签对Spring默认标签的解析做了详解,在xml元素的解析中,Spri ...
- Spring源码学习-容器BeanFactory(五) Bean的创建-探寻Bean的新生之路
写在前面 上面四篇文章讲了Spring是如何将配置文件一步一步转化为BeanDefinition的整个流程,下面就到了正式创建Bean对象实例的环节了,我们一起继续学习吧. 2.初始化Bean对象实例 ...
- Spring源码学习之BeanFactory体系结构
一.BeanFactory BeanFactory是Spring IOC容器的鼻祖,是IOC容器的基础接口,所有的容器都是从它这里继承实现而来.可见其地位.BeanFactory提供了最基本的IOC容 ...
- spring源码学习五 - xml格式配置,如何解析
spring在注入bean的时候,可以通过bean.xml来配置,在xml文件中配置bean的属性,然后spring在refresh的时候,会去解析xml配置文件,这篇笔记,主要来记录.xml配置文件 ...
- Spring源码学习(2)——默认标签的解析
上一篇随笔说到Spring对于默认标签和自定义标签的解析方法是不同的,这里详细看一下Spring对于默认标签的解析. private void parseDefaultElement(Element ...
- Spring 源码学习 04:初始化容器与 DefaultListableBeanFactory
前言 在前一篇文章:创建 IoC 容器的几种方式中,介绍了四种方式,这里以 AnnotationConfigApplicationContext 为例,跟进代码,看看 IoC 的启动流程. 入口 从 ...
- spring源码学习之路---IOC初探(二)
作者:zuoxiaolong8810(左潇龙),转载请注明出处,特别说明:本博文来自博主原博客,为保证新博客中博文的完整性,特复制到此留存,如需转载请注明新博客地址即可. 上一章当中我没有提及具体的搭 ...
随机推荐
- Linux中查看端口占用情况
1.lsof -i:端口号 用于查看某一端口的占用情况,比如查看8000端口的使用情况: # lsof -i:8000 2.netstat -tunlp | grep 端口号,用于查看指定的端口号的进 ...
- CNN的反向传播
在一般的全联接神经网络中,我们通过反向传播算法计算参数的导数.BP 算法本质上可以认为是链式法则在矩阵求导上的运用.但 CNN 中的卷积操作则不再是全联接的形式,因此 CNN 的 BP 算法需要在原始 ...
- Saltstack自动化操作记录(1)-环境部署【转】
早期运维工作中用过稍微复杂的Puppet,下面介绍下更为简单实用的Saltstack自动化运维的使用. Saltstack知多少Saltstack是一种全新的基础设施管理方式,是一个服务器基础架构集中 ...
- javascript闭包学习
(function(){})()===>>>>函数会被立即执行function(){}是一个函数用括号包起来表示是函数表达式再加()表示函数自执行 如何理解闭包?1.定义和用 ...
- Linux kill 命令 以及USR1 信号解释
kill 中的USR信号解释 USR1亦通常被用来告知应用程序重载配置文件:例如,向Apache HTTP服务器发送一个USR1信号将导致以下步骤的发生:停止接受新的连接,等待当前连接停止,重新载入配 ...
- COGS2187 [HZOI 2015] 帕秋莉的超级多项式
什么都别说了,咱心态已经炸了... question 题目戳这里的说... 其实就是叫你求下面这个式子的导函数: noteskey 其实是道板子题呢~ 刚好给我们弄个多项式合集的说... 各种板子粘贴 ...
- linux常见故障处理
目录 一. 文件和目录类 1.1 File exist 文件已经存在 1.2 No such file or directory 没有这个文件或目录(这个东西不存在) 1.3 command not ...
- Docker----与Asp.net core 的完美结合,在docker容器中创建Asp.Net Core 项目
在腾讯云上买了一个小容量的服务器,搭建一个docker环境后,可以尝试做一些单系统做起来很麻烦的东西.譬如说,你在windows OS或UbuntuOS中,突然想玩CentOS了,你可以选择将电脑再装 ...
- 末学者笔记——SAMBA服务、FTP服务讲解
samba服务 一.概念: Smb主要作为网络通信协议; Smb是基于cs(client,sever)架构: 完成Linux与windows之间的共享:linux与linux之间共享用NFS 二. ...
- .net core 使用swagger自动生成接口文档
前言 swagger是一个api文档自动生动工具,还集成了在线调试. 可以为项目自动生成接口文档, 非常的方便快捷 Swashbuckle.AspNetCore 是一个开源项目,用于生成 ASP.N ...