写在前面

从大四实习至今已一年有余,作为一个程序员,一直没有用心去记录自己工作中遇到的问题,甚是惭愧,打算从今日起开始养成写博客的习惯。作为一名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,还有常见的URLResourceFileSystemResource等等。如果我们项目中有需要定位的资源文件,也可以考虑使用上面几个类,通过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的创建-解析资源文件的更多相关文章

  1. Spring源码学习-容器BeanFactory(二) BeanDefinition的创建-解析前BeanDefinition的前置操作

    写在前面 上文 Spring源码学习-容器BeanFactory(一) BeanDefinition的创建-解析资源文件主要讲Spring容器创建时通过XmlBeanDefinitionReader读 ...

  2. Spring源码学习-容器BeanFactory(三) BeanDefinition的创建-解析Spring的默认标签

    写在前面 上文Spring源码学习-容器BeanFactory(二) BeanDefinition的创建-解析前BeanDefinition的前置操作中Spring对XML解析后创建了对应的Docum ...

  3. Spring源码学习-容器BeanFactory(四) BeanDefinition的创建-自定义标签的解析.md

    写在前面 上文Spring源码学习-容器BeanFactory(三) BeanDefinition的创建-解析Spring的默认标签对Spring默认标签的解析做了详解,在xml元素的解析中,Spri ...

  4. Spring源码学习-容器BeanFactory(五) Bean的创建-探寻Bean的新生之路

    写在前面 上面四篇文章讲了Spring是如何将配置文件一步一步转化为BeanDefinition的整个流程,下面就到了正式创建Bean对象实例的环节了,我们一起继续学习吧. 2.初始化Bean对象实例 ...

  5. Spring源码学习之BeanFactory体系结构

    一.BeanFactory BeanFactory是Spring IOC容器的鼻祖,是IOC容器的基础接口,所有的容器都是从它这里继承实现而来.可见其地位.BeanFactory提供了最基本的IOC容 ...

  6. spring源码学习五 - xml格式配置,如何解析

    spring在注入bean的时候,可以通过bean.xml来配置,在xml文件中配置bean的属性,然后spring在refresh的时候,会去解析xml配置文件,这篇笔记,主要来记录.xml配置文件 ...

  7. Spring源码学习(2)——默认标签的解析

    上一篇随笔说到Spring对于默认标签和自定义标签的解析方法是不同的,这里详细看一下Spring对于默认标签的解析. private void parseDefaultElement(Element ...

  8. Spring 源码学习 04:初始化容器与 DefaultListableBeanFactory

    前言 在前一篇文章:创建 IoC 容器的几种方式中,介绍了四种方式,这里以 AnnotationConfigApplicationContext 为例,跟进代码,看看 IoC 的启动流程. 入口 从 ...

  9. spring源码学习之路---IOC初探(二)

    作者:zuoxiaolong8810(左潇龙),转载请注明出处,特别说明:本博文来自博主原博客,为保证新博客中博文的完整性,特复制到此留存,如需转载请注明新博客地址即可. 上一章当中我没有提及具体的搭 ...

随机推荐

  1. Python学习笔记-Django连接SQLSERVER

    Django连接SQLSERVER使用的是odbc驱动. CentOS下安装django-obdc-azure时需安装依懒 yum install gcc yum install gcc-c++ yu ...

  2. 【转】pyhton之Reportlab模块——生成pdf文件

    [转]pyhton之Reportlab模块 reportlab模块是用python语言生成pdf文件的模块 安装:pip install reportlab 模块默认不支持中文,如果使用中文需要注册 ...

  3. vue面试题总结

    1.vue双向绑定的实现原理2.js的继承和原型链3.es6语法箭头函数和普通函数的区别 普通函数的this总是指向它的直接调用者. 在严格模式下,没找到直接调用者,则函数中的this是undefin ...

  4. Linux搭建NodeJs环境

    文件下载与解压 文件下载 wget https://npm.taobao.org/mirrors/node/v6.10.3/node-v6.10.3-linux-x64.tar.xz 如果要下载最新版 ...

  5. js replace替换字符串,同时替换多个方法

    在实际开发中,经常会遇到替换字符串的情况,但是大多数情况都是用replace替换一种字符串,本文介绍了如何使用replace替换多种指定的字符串,同时支持可拓展增加字符串关键字. let conten ...

  6. OpenCV中的KNN

    一.K近邻 有两个类,红色.蓝色.我将红色点标记为0,蓝色点标记为1.还要创建25个训练数据,把它们分别标记为0或者1.Numpy中随机数产生器可以帮助我们完成这个任务 import cv2 impo ...

  7. WordPress 文章点赞

    Installation 上传 wp-zan目录 到 /wp-content/plugins/ 目录 在后台插件菜单激活该插件 添加 <?php wp_zan();?> 到需要的位置 De ...

  8. Emacs中的拼写检查

    无论是在Emacs中写英文日记(diary).Org mode笔记,还是撰写程序的注释和文档,拼写检查都是一项提高工作效率.保证成果品质的必不可缺的工具.拼写检查对于常见的文字处理软件(如Word.L ...

  9. C# 获取版本号

    Text = Text + " [V." + Assembly.GetExecutingAssembly().GetName().Version + "]";

  10. .NET英文技术文章导读(2017-03-23)

    关键字:VS2017.扩展.Service Fabric.Unit Test.ELMAH Web开发人员必装的5个VS2017扩展 作者:Jeffrey T. Fritz 链接:https://blo ...