写在前面

从大四实习至今已一年有余,作为一个程序员,一直没有用心去记录自己工作中遇到的问题,甚是惭愧,打算从今日起开始养成写博客的习惯。作为一名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. CentOS搭建GIT服务器

    安装git # 请确保您切换到了root账户 $ su root $ yum install -y git # 验证是否安装成功 $ git --version # 输出如下内容表示成功: git v ...

  2. 使用ansible实现轻量级的批量主机管理

    作者:邓聪聪 查看ansible配置文件下的hosts的文件 [root@ansible-server scripts]# cat /etc/ansible/hosts [test] 172.16.1 ...

  3. centos6.8编译安装mysql

    1.安装编译代码需要的包 yum -y install make gcc-c++ cmake bison-devel ncurses-devel 2.创建mysql用户(但是不能使用mysql账号登陆 ...

  4. Centos安装Git、DotNet、Docker

    1.安装Git yum install git 可通过下面的命令查看Git版本 git --version 2.安装Dotnet sudo yum install libunwind libicu 导 ...

  5. nginx配置文件,做多个项目代理

    web01: server { listen 9988; server_name www.oldboy.com; access_log logs/www.oldboy.com_access.log m ...

  6. poj1456 Supermarket 贪心+并查集

    题目链接:http://poj.org/problem?id=1456 题意:有n个物品(0 <= n <= 10000) ,每个物品有一个价格pi和一个保质期di (1 <= pi ...

  7. 设计模式 — 简单工厂模式(staticFactory)

    这篇博文介绍简单工厂模式,设计模式并不是固定的二十三种,不同的书介绍的可能有出入,这篇介绍的简单工厂模式在有些书上就忽略不介绍了.设计模式是一套被反复使用的.多数人知晓的.经过分类编目的.代码设计经验 ...

  8. Java模仿http请求工具类

    package ln; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamRea ...

  9. JS & JQuery 动态处理select option

    原文 出处http://www.51xuediannao.com/html+css/htmlcssjq/cssbuhuanhang.html 今天你问了我一个关于在<select>里动态添 ...

  10. 【Idea】好的插件集合,持续更新

    UploadJar,用于配合Nexus上传jar包,方便上传 Key Promoter X,用于显示快捷键,学习快捷键非常实用 lombok,getter/setter使用注解,而不需要写 自动生成g ...