Spring源码深度解析(1)——加载Bean

之前提到的在XMLBeanFactory构造函数中调用了XmlBeanDefinitionReader类型的reader属性提供的方法this.reader.loadBeanDefinitions(resource),而这句代码则是整个资源加载的切入点。

  • XMLBeanFactory初始化时序图

在这里插入图片描述

  • loadBeanDefinitions方法的时序图

在这里插入图片描述

从这个开始看!

Bean bf = new XmlBeanFactory(new ClassPathResource("test.xml");

在测试的BeanFactoryTest中首先调用ClassPathResource的构造函数来构造Resource资源文件的实例对象,这样后续的资源处理就可以用Resource提供的各种服务来操作了,当我们有了Resource后就可以进行XmlBeanFactory的初始化。

ClassPathResource

1.带一个参数的构造方法

在这里插入图片描述

上面的构造方法调用下面的带两个参数的构造方法

2.带两个参数的构造方法
在这里插入图片描述

将path的默认的calssLoader封装起来

XMLBeanFactory

3.带一个和两个参数的构造方法

在这里插入图片描述

有了Resource接口便可以对所有资源文件进行统一处理。至于实现,其实非常简单,以getInputStream为例子,ClassPathResource中的实现方式是通过class或者classLoader提供的底层方法进行调用。

  • XMLBeanDefinitionReader类下的

用于对资源文件的编码进行处理
在这里插入图片描述

该方法调用EncodeResource类的构造方法

在这里插入图片描述

在这里插入图片描述

EncodeResource类中返回回来继续看XmlBeanDefinitionReader类下的loadBeanDefinitions方法。

在这里插入图片描述

注意第【337】行代码,才进入真正的逻辑核心部分。

再看看这个loadBeanDefinitions方法执行的时序图

在这里插入图片描述

从上面的时序图中我们可以总结梳理出如下处理流程。

  1. 封装资源文件。
    当进入XmlBeanDefinitionReader后首先对参数Resource使用EncodedResource类进行封装。
  2. 获取输入流
    Resource中获取对应的InputStream并构造InputSource
  3. 通过构造的InputSource实例和Resource实例继续调用函数doLoadBeanDefinitions

图7中调用图8中的方法,该方法大部分代码都是异常处理,主要的方法就在第【392】和【393】行。

在这里插入图片描述

第【392】行做了两件事情,但是这两件事情是调用doLoadDocument方法类实现的,因此我们继续看doLoadDocument方法

在这里插入图片描述

这个方法做了两件事情:

  1. 获取对Xml文件的验证模式
  2. 加载对应xml文件,并得到对应的Document。

获取XML的验证模式

DTDXSD区别

在这里插入图片描述

在使用 XML Schema 文档对XML实例文档进行验证,除了要声明空间外(xmlns = "http://www.springframework.org/schema/beans"),还必须制定该名称空间所对应的 XML Schema 文档的存储位置。通过 schemaLocation 属性来指定名称空间锁对应的 XML Schema 文档的存储位置。它包含两个部分,一部分是名称空间的 URI,另一部分就是该名称空间锁标识的 XML Schema 文件位置或URL地址(xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd

从图8.1中我们可以看到,doLoadDocument方法实现XML文件的验证是通过调用getValidationModeForResource方法来获取对应资源的验证模式。接下来我们看getValidationModeForResource方法的源码。

在这里插入图片描述

从图8.2中可以看出来,验证模式分为两种:一种是手动指定、另一种是自动检测。而自动检测验证模式的功能时在函数detectValidationMode方法中实现的,在detectValidationMode中又将自动检测验证模式的工作委托给了专门处理类validationModeDetector.detectValidationMode,我们一层一层往下看。先看detectValidationMode方法。

在这里插入图片描述

从【第487行】可以看出,这个detectValidationMode方法是一个逻辑层面的方法,主要的XML验证是由return this.validationModeDetector.detectValidationMode(inputStream);实现的。

在这里插入图片描述

从上面代码也可以看出来,Spring用来检测验证模式的办法就是判断是否包含 DOCTYPE,如果包含就是 DTD,否则就是 XSD

获取Document

经过了验证模式准备的步骤就可以进行Document加载了,验证模式准备在【图8.1】中,同样 XmlBeanFactoryReader类对于文档读取并没有亲力亲为,而是委托给了DocumentLoader去执行,这里的DocumentLoader是一个接口,而真正调用的是DefaultDocumentLoader

在这里插入图片描述

在这里插入图片描述

注意这个方法一共有5个参数。

  • InputSource
  • EntityResolver
    • 对于这个参数,传入的是通过getEntityResolver方法获取的返回值
  • ErrorHandler
  • int validationMode
  • boolean namespaceAware

为了解释EntityResolver参数,我又把【图8.1】拿回来再显示一遍。

在这里插入图片描述

现在我们看看它是如何从getEntityResolver中获取的数据。

这里先不具体的分析它是怎么解析出来的了,我们主需要知道下面这段话就可以了:

如果SAX 应用程序需要实现自定义处理外部实体,则必须实现此接口并使用 setEntityResolver方法向 SAX驱动器注册一个实例。也就是说,对于解析一个XML, SAX首先读取该XML文档上的声明,根据声明去寻找响应的DTD定义,以便对文档进行一个验证。默认的寻找规则,即通过网络(实现上就是声明的DTD的URI地址)来下载响应的DTD声明,并进行认证。下载的过程是一个漫长的过程,而且当网络中断或不可用时,这里会报错,就是因为响应的DTD声明没有被找到的原因。

======================================================================================================

解析及注册BeanDefinitions

至此,我们已经把【图8】中调用的第一个方法的功能分析完了,接下来我们分析第二个功能,我再将【图8】重新贴一下。

在这里插入图片描述

现在我们来分析调用的第二个方法registerBeanDefinitions

在这里插入图片描述

BeanDefinitionDocumentReader 是一个接口,而实例化的工作是在 createBeanDefinitionDocumentReader() 中完成的,而通过此方法, BeanDefinitionDocumentReader 真正的类型其实已经是 DefaultBeanDefinitionDocumentReader 了,进入 DefaultBeanDefinitionDocumentReader 后,发现这个方法的重要目的之一就是提取 root ,以便于再次将 root 作为参数继续 BeanDefinition 的注册。

在这里插入图片描述

接下来找到BeanDefinitionDocumentReader的实现类DefaultBeanDefinitionDocumentReader

在这里插入图片描述

如果说以前一直是 XML 加载解析的准备阶段,那么 doRegisterBeanDefinitions 算是真正地开始进行解析了。

在这里插入图片描述

如果继承自 DefaultBeanDefinitionDocumentReader 的子类需要在 Bean 解析前后做一些处理的话,那么只需要重写这两个方法就可以了。

profile 属性的使用

该特性保证我们可以同时在配置文件中部署两套配置来适用于生产环境和开发环境,这样可以方便的进行切换开发、部署环境,最常用的就是更换不同的数据库。

上面【图9.3】的执行过程如下:首先程序会获取 beans 字节是否定义了 profile 属性,如果定义了则会需要到环境变量中去寻找,所以这里首先断言 encironment 不可能为空格,因为 profile 是可以同时制定多个的,需要程序对其拆分,并解析每个 profile 是都符合环境变量中所定义的,不定义则不会浪费性能去解析。

在这里插入图片描述

在下一节 Spring源码深度解析(2)中会继续深入解析parseBeanDefinitions方法

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值