分析以下功能的代码实现:
BeanFactory bf = new XmlBeanFactory(new ClassPathResource("beanFactory.xml"));
1.ClassPathResource类的作用
首先分析new ClassPathResource(“beanFactory.xml”),此句是通过ClassPathResource类将beanFactor.xml文件封装为一个具有统一结构的Resource对象(Resource为一接口,提供了一些方法用以检查当前资源是否存在、是否可读。对于不同类型的资源,Resource有着不同的实现。比如对于文件File类型的资源来说,其封装类为FileSystemResource类)。
其中,Resource类继承自InputStream,提供一**getInputStream()**方法以获得InputStream输入流,使得后续处理方法可通过Resource获得beanFactor.xml文件的文本内容(只能获取其字符信息,无法解析其意义,“能读但读不懂”)。
补充:其实Resource中getInputStream()所提供的输入流也是通过jdk的底层方法调用的,其根本就是获得一个输入流来读取文件,和平时的读取操作别无二致。如对于FileSystemResource类,其getInputStream()方法返回的InputStream就是:FileInpurStream(this.file)。
//**FileSystemResource.java**
public InputStream getInputStream() throws IOException{
return new FileInputStream(this.file);
}
2.解析XML及注册BeanDefinition
得到Resource对象之后,XMLBeanFactory调用一个也十分重要的类:XMLBeanDefinitionReader类的方法this.reader.loadDefinitions(resource)。此句代码便是整个资源加载的切入点,此方法及其嵌套调用的各种方法完成了对XML文件的解析及BeanDefinition的解析和注册。接下来,详细分析此方法的处理过程。
(1)使用EncodedResource类对Resource对象进行封装
当进入XMLBeanDefinitionReader后首先对参数Resource使用EncodedResource类进行封装。对于该Resource对象,采用我们设置的编码方式进行编码,使得后续处理时可以获得我们期望的编码输入流。
(2)获取输入流
从Resource中获取对应的InputStream并构造InputSource。(为了后续使用sax读取XML文件,所有的sax API都从InputSource开始)。
(3)调用函数doLoadBeanDefinitions
通过构造的InputSource实例和Resource实例调用doLoadBeanDefinitions(inputSource,encodedResource.getResource())。其中,此函数的主要逻辑有三部分:
①. 获取XML文件的验证模式(DTD/XSD)
int validationMode=getValidationModeForResource(resource); //获取文件验证模式
验证模式可手动设定,手动设定后,上述方法得到的验证模式就是该设定值。若无手动设定,则开启自动检测验证模式,使用detectValidationMode()方法,该方法主要检测XML文档中是否包含“DOCTYPE”字符串,包含则使用DTD验证模式,不包含则使用XSD验证模式。另一些细节问题可参照《Spring源码深度解析》P26-P27.
②.获取Document
XmlBeanFactoryReader委托给DocumentLoader去执行,DocumentLoader是个接口,真正调用的是DefaultDocumentLoader。首先创建DocumentBuilderFactory,再通过DocumentBuilderFactory创建DocumentBuilder,进而解析InputSource来返回Document对象。
Document对象介绍:使用上述的解析器后,会将XML文件解析为一结点树,树中各结点对应XML中的一个标签。如根结点就是XML中的根标签< beans>。根结点的子结点对应根标签的子标签,以此类推。Document其实就是对应XML的整个文档。通过根结点可找到所有的子结点,方便后续对结点进行进一步解析。
③解析并注册BeanDefinition
获取Document对象后,XmlBeanDefinitionReader调用:registerBeanDefinitions(Document doc,Resource resource)方法,此方法又委托给单一的类(BeanDefinitionDocument类)进行处理(单一职责模式)。
在实现了BeanDefinitionDocumentReader类接口的DefaultBeanDefinitionDocumentReader的方法registerBeanDefinitions(Document doc, XmlReaderContext readerContext)中提取doc对象的根结点(对应XML的根标签),并将此根结点传入doRegisterBeanDefinitions(Element root)方法,此方法为核心逻辑代码。
在doRegisterBeanDefinition方法中,首先完成的是对Profile属性的处理。Profile属性可以方便的进行切换开发部署环境。接下来,真正开始解析标签,进入parseBeanDefinition(Element root , BeanDefnitionParserDelegate delegate)方法。
parseBeanDefinition()方法完成对结点(标签)的解析。首先检验root是否为默认根标签,若是默认根标签,则遍历其各子结点进行解析(子标签也区分是否为默认标签),若是自定义根标签则转入自定义标签解析方法。