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 kernel 通知链(notifier chain)【转】
转自:https://blog.csdn.net/u014134180/article/details/86563754 版权声明:本文为博主原创文章,未经博主允许不得转载.——Wu_Being ht ...
- NBIoT三种部署方式【转】
转自:https://472880.kuaizhan.com/89/34/p441944286fccf2 本文作者:吴老司撩通信 本文来源:EETOP NB-IoT支持在频段内(In-Band).保护 ...
- Linux apache的运行用户和用户组
我们在安装apache后,有时在上传文件的时候,提示没有权限或者是不可写,我们都会去查文件夹的权限. 通过ls -l /var/www/html/website可以很直观的看出我们文件和文件夹的权限, ...
- CentOs7.5安装PostgreSQL11
前言 本章介绍在CentOs上安装一个PostgreSQL数据库 下一篇可能是安装 Redis 本篇使用的服务器是已经安装过Python/Nginx等常用软件的环境,因此在安装过程中可能会遇到按照本章 ...
- JAVA This的简单运用
package Code426;/*当方法的局部变量和类的成员变量重名时,根据“就近原则”,优先使用局部变量如果需要访问本类当中的成员变量,需要使用 this.成员变量 通过谁调用的方法,谁就是thi ...
- PHP斐波那契数列有点不明白有哪位大佬能给讲讲思想以及实现过程
function Fibonacci($n){ if($n <= 0) { return 0; }elseif($n == 1) { ...
- jsp中一个标签两种方式绑定两个click事件导致未执行的问题
近日,在开发过程中,写了一个标签 <li id="a1" onclick="doSomething()">...</li> 在js页面中 ...
- Security配置文件的基本配置及参数名详解
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.spr ...
- C++智能指针剖析(下)boost::shared_ptr&其他
1. boost::shared_ptr 前面我已经讲解了两个比较简单的智能指针,它们都有各自的优缺点.由于 boost::scoped_ptr 独享所有权,当我们真真需要复制智能指针时,需求便满足不 ...
- shell实用
nginx日志切割 [root@localhost ~]# vim /opt/fenge.sh #!/bin/bash #Filename:fenge.sh d=$(date -d "-1 ...