天天看点

Spring源码剖析——Bean的配置与启动

  相信大多数人在学习spring时 ioc 和 bean 算得上是最常听到的两个名词,ioc在学习spring当中出现频率如此之高必然有其原因。如果我们做一个比喻的话,把bean说成spring中主角的话,那么ioc便是这个主角进行演出的舞台,没有ioc作为bean的承载,那么bean便不会在编程中大放异彩。作为spring核心组件的重要一员,了解其内部实现对我们编程和窥探spring内幕是相当有帮助的,下面一步步从源码的角度来剖析ioc究竟是怎样实现的。

  首先我们先通过一张接口设计图了解下,ioc容器整体的设计架构

Spring源码剖析——Bean的配置与启动

  通过上面的接口设计图我们可以看到,在spring当中最基本的ioc容器接口是beanfactory,这个接口当中定义作为一个ioc容器最基本的一些操作和功能,下来看看它的源码:

  通过上面的接口说明我们看到在beanfactory里面只对ioc容器最基本的行为做了定义,而不关心bean是怎样定义和加载的。如果我们想要知道一个工厂具体生产对象的过程,就需要去看这个接口的实现类,在spring当中实现这个接口的有很多子类,下面我们来看看其一个常用的实现类xmlbeanfacotry的代码吧。

  我们可以看到要构造xmlbeanfactory对象的话需要一个resource对象,这个resource是什么呢?

简单的讲resource接口是为了提供更强的访问底层资源能力的抽象,它是spring访问资源最基本的接口。

  通过xmlbeanfactory我们也可以大致可以猜到,对于该类的要传的resource对象便是一个xml的文件流。那么,这下我们知道这个beanfactory的简单实现类的用法,也知道了构造这个对象的的参数,先动手实践创建一个最原始的ioc容器吧。

1、实体类user.java

2、xml资源文件beans.xml

  使用单步调试了解上面代码的主要内部调用流程,这里没有跟踪getbean()方法的过程,该步骤后设计bean的解析过程,后面我们再说这个

调用顺序

类名

方法名

1

xmlbeanfactory

构造方法(resource)

2

defaultlistablebeanfactory

构造方法(beanfactory)

3

abstractautowirecapablebeanfactory

4

setparentfactory(beanfactory)

5

xmlbeandefinitionreader

loadbeandefinitions(resource)

6

abstractbeandefinitionreader

getresourceloader()

通过上面的流程,我们可以简单的将使用ioc容器的步骤概括如下:

  我们对这个内部流程有了了解了以后,下面可以在实践当中进行应用,我们之前写的mysimplebeanfactory代码便可以更改为下面的形式:

  两者的作用是等效的。

  我们现在对ioc大体上有了初步了解后,下面对一些关键点进行细节上的一些分析。首先,从我们第一步创建的classpathresource对象说起,重新回过头来认识spring当中的resource。

  在上面的文章我们也简单提到过 resource 接口的作用,其实spring使用自己的抽象结构对整个框架中所用到资源进行统一的封装,封装这个接口的原因其实大家也大致可以想到,我们在编程当中所遇到的资源文件类型多种多样有file、inputstream、urlresource、contextresource等等,面对如此多的资源类型框架内部要是不设计出一套成熟合理的资源封装体系的话,无疑对框架的实现和开发者的编程来说造成很多的复杂性。面向对象编程当中的封装和继承特性,在spring当中可谓应用的淋漓尽致,有时间自己可以花点功夫体会spring框架这一套设计体系对我们的以后编写可扩展和可用代码有很大的好处。

  继续展开上面resource接口的介绍,该接口作为资源的一个原始封装,它继承自inputstreamresource接口,inputstreamresource只有<code>inputstream getinputstream()</code>这样一个方法,通过这次继承赋予了通过resource对象获得inputstram流的功能。下面看看接口的源码:

  再看看我们上面代码当中所用到的resource接口的实现类classpathresource的部分需要重点关注的源码

  我们已经把资源层面上的接口和实现了解了,下面分析我们之前代码当中的<code>defaultlistablebeanfactory factory = new defaultlistablebeanfactory();</code>这句。在这里我们有引入了一个新类defaultlistablebeanfactory,从ioc类图中可以看到这个是beanfactory的一个默认实现类,其实例对象可以作为一个独立使用的ioc容器,可以认为该对象是容纳bean对象的一个容器,我们定义好的bean对象经过spring的载入和加载等处理后,最终交由改工厂进行管理,我们需要得到bean对象时便可以向它获取。

  我们调用其无参构造方法创建了factory对象,看看底层都做了哪些事情。

接下来跟踪父类abstractautowirecapablebeanfactory中源码

  上面我们创建好了 defaultlistablebeanfactory 对象,已经有了容纳bean的容器了,但我们通过刚才分析源码可以看到现在我们还没有对该factory进行其他实用性的操作,里面还没有任何东西。那么,我们定义在配置文件定义的bean是如何进入spring容器当中的,就得继续分析下面的代码了。我们先分析<code>xmlbeandefinitionreader reader = new xmlbeandefinitionreader(factory);</code>的实现。

  从xmlbeandefinitionreader类名中我们又见到一个新名词beandefinition,我们先了解下spring当中beandefinition的作用。

  接着我们看xmlbeandefinitionreader类中关键部分的源码

上面的源码涉及到了对xml加载的过程,而对于一个xml的加载可以将其分为三步

1.读取xml文件的验证模式,即判读其使用的是dtd还是xsd

2.依据上面的验证模式对xml文件进行验证

3.加载xml文件

上面的过程在源码的体现如下:

  通过以上的验证准备,就可以对xml文件进行加载了。而加载的过程xmlbeandefinitionreader并没有自己去完成,而是由documentloader接口去完成的。跟踪源码我们发现具体的实现是<code>defaultdocumentloader.loaddocument()</code>,源码体现如下

接着看defaultdocumentloader中的方法实现

  通过这个方法便可以获得xml的document对象了。

  现在我们已经获得xml解析后的document对象,接下来只要在对该document结构进行分析便可以知道bean在xml中是如何定义的,也就能将其转换为beandefinition对象。

  这个过程调用的是<code>xmlbeandefinitionreader类的registerbeandefinitions(document doc, resource resource)</code>方法,代码如下:

  上面的过程就不仔细分析了,感兴趣的可以自行阅读beandefinitionparserdelegate的实现,主要逻辑就是根据不同的标签调用相应的处理流程对完成对beandefinition的解析。这步处理完成了,离我们的目标也就不远啦。

  我们配置的bean的信息经过解析在spring内部已经转换为beandefinition这种统一的结构,但这些数据还不能供ioc容器直接使用,需要在ioc容器中对这些beandefinition数据进行注册。

  注册过程调用的是defaultlistablebeanfactory的<code>registerbeandefinition</code>方法

  上面的注册操作完毕后,一个最简单的ioc容器就可用使用啦,我们今天分析工作也就完工了。看到这里是不是有点头昏脑涨了~(&gt;_&lt;)~,要做到了解spring内部结构并不是一件一朝一夕的事,最好的学习方法就是阅读了文章对大体流程有了了解后,自己单步去跟踪学习。最近初步涉猎spring源码,自己写的过程中若有不足之处,还望大家及时反馈。