《Spring技术内幕》学习笔记3——IoC容器载入Bean定义资源文件
1.当Spring的IoC容器将Bean定义的资源文件封装为Spring的Resource之后,接下来要做的就是通过Spring的资源加载器(resourceLoader)读入Bean定义资源文件的过程。对于IoC容器来说,Bean定义的载入过程就是将Bean定义资源文件读入进内存并解析转换成Spring所管理的Bean的数据结构的过程。相对于SpringIoC容器定位Bean定义资源文件来说,B
1.当Spring的IoC容器将Bean定义的资源文件封装为Spring的Resource之后,接下来要做的就是通过Spring的资源加载器(resourceLoader)读入Bean定义资源文件的过程。对于IoC容器来说,Bean定义的载入过程就是将Bean定义资源文件读入进内存并解析转换成Spring所管理的Bean的数据结构的过程。相对于SpringIoC容器定位Bean定义资源文件来说,Bean定义资源文件的载入和解析过程更复杂一些,因此按照程序的运行步骤逐条分析其实现原理。
2.以FileSystemXmlApplicationContext为例分析其载入和解析Bean定义资源文件的过程:
首先从FileSystemXmlApplicationContext的入口构造函数分析起,其代码如下:
Spring IoC容器对Bean定义资源的载入是从refresh()函数开始的,FileSystemXmlApplicationContext通过调用其父类AbstractApplicationContext的refresh()函数启动整个IoC容器对Bean定义的载入过程。
3.AbstractApplicationContext的refresh函数载入Bean定义过程:
refresh()方法主要为IoC容器Bean的生命周期管理提供条件,Spring IoC容器载入Bean定义资源文件从其子类容器的refreshBeanFactory()方法启动,所以整个refresh()中“ConfigurableListableBeanFactory beanFactory =obtainFreshBeanFactory();”这句以后代码的都是注册容器的信息源和生命周期事件,载入过程就是从这句代码启动。
AbstractApplicationContext的obtainFreshBeanFactory()方法调用子类容器的refreshBeanFactory()方法,启动容器载入Bean定义资源文件的过程,代码如下:
4.AbstractApplicationContext子类的refreshBeanFactory()方法:
AbstractApplicationContext类中只抽象定义了refreshBeanFactory()方法,容器真正调用的是其子类AbstractRefreshableApplicationContext实现的refreshBeanFactory()方法,方法的源码如下:
refresh()方法的作用是:在创建IoC容器前,如果已经有容器存在,则需要把已有的容器销毁和关闭,以保证在refresh之后使用的是新建立起来的IoC容器。refresh的作用类似于对IoC容器的重启,在新建立好的容器中对容器进行初始化,对Bean定义资源进行载入。
和refreshBeanFactory方法类似,载入Bean定义的方法loadBeanDefinitions也使用了委派模式,在AbstractRefreshableApplicationContext类中只定义了抽象方法,具体的实现调用子类容器中的方法实现。
5.AbstractRefreshableApplicationContext子类的loadBeanDefinitions方法:
AbstractRefreshableApplicationContext中只定义了抽象的loadBeanDefinitions方法,容器真正调用的是其子类AbstractXmlApplicationContext对该方法的实现,AbstractXmlApplicationContext的主要源码如下:
Xml Bean读取器(XmlBeanDefinitionReader)调用其父类AbstractBeanDefinitionReader的 reader.loadBeanDefinitions方法读取Bean定义资源。
由于我们使用FileSystemXmlApplicationContext作为例子分析,因此getConfigResources的返回值为null,因此程序执行reader.loadBeanDefinitions(configLocations)分支。
6. AbstractBeanDefinitionReader读取Bean定义资源:
AbstractBeanDefinitionReader的loadBeanDefinitions方法源码如下:
loadBeanDefinitions(Resource...resources)方法和上面分析的3个方法类似,同样也是调用XmlBeanDefinitionReader的loadBeanDefinitions方法。
从对AbstractBeanDefinitionReader的loadBeanDefinitions方法源码分析可以看出该方法做了以下两件事:
首先,调用资源加载器的获取资源方法resourceLoader.getResource(location),获取到要加载的资源。
其次,真正执行加载功能是其子类XmlBeanDefinitionReader的loadBeanDefinitions方法。
7.资源加载器获取要读入的资源:
XmlBeanDefinitionReader通过调用其父类DefaultResourceLoader的getResource方法获取要加载的资源,其源码如下:
FileSystemXmlApplicationContext容器提供了getResourceByPath方法的实现,就是为了处理既不是classpath标识,又不是URL标识的Resource定位这种情况。
现在,Bean定义的Resource得到了,下面我们继续跟随程序执行方向,分析XmlBeanDefinitionReader的loadBeanDefinitions方法。
8. XmlBeanDefinitionReader加载Bean定义资源:
通过源码分析,载入Bean定义资源文件的最后一步是将Bean定义资源转换为Document对象,该过程由documentLoader实现。
9. DocumentLoader将Bean定义资源转换为Document对象:
DocumentLoader将Bean定义资源转换成Document对象的源码如下:
该解析过程调用JavaEE标准的JAXP标准进行处理。
至此Spring IoC容器根据定位的Bean定义资源文件,将其加载读入并转换成为Document对象过程完成。
10.看源代码的个人心得总结:
通过这几天看源码,个人总结一些心得:代码毕竟不是文章,不能从头到尾详细看,个人觉得看源码比较好的方法是:
首先,先实现一个简单例子调用源码,让整个应用能简单跑起来。
然后,在进入代码最开始的地方打一个调试断点,使用Debug工具进行单步调试,直到跑完所有流程。
通过调试就可以理解整个代码的工作流程和调用顺序,有助于理清思路,理解其大概的设计思想。
Spring代码的确实比较复杂,代码中大量使用了设计模式,另外为了解耦合,代码的分工比较明确,对象也非常的多,对于没有分析源码经验的人来说,阅读代码发现其跨度和跳转非常大,难度和挑战比较大。
我也是第一次分析Spring源码,希望和大家一起学习探讨。
更多推荐
所有评论(0)