Spring源码1 配置文件的读取

本文的内容是读郝佳的《Spring源码深度解析》整理的笔记。

文章目录

      • 入门实例
      • 读取spring-config.xml配置文件
        • Resource类
        • Document类
        • 浅谈BeanDefinition注册

入门实例

书中通过一个最简单入门实例,对配置文件的加载、配置文件中标签的解析、bean的加载部分的源码进行了剖析。书中的例子是基于XmlBeanFactory来创建BeanFactory,但是这个在类在Spring5.0.x中已经过时,这里对该实例稍作改动,基于Spring5.0.x实现。

  • 首先创建一个Maven项目,pom.xml导入spring-beans.
<dependency>
    <groupId>org.springframeworkgroupId>
    <artifactId>spring-beansartifactId>
    <version>5.0.15.RELEASEversion>
dependency>
  • 创建MyBean
public class MyBean {
    private String testStr= "testStr";
    public String getTestStr() {
        return testStr;
    }
    public void setTestStr(String testStr) {
        this.testStr = testStr;
    }
}
  • 创建spring-config.xml

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="myTestBean" class="MyBean" scope="prototype"/>

beans>
  • 测试类
public class TestMyBean {
    @Test
    public void testSimpleLoad(){
        Resource resource = new ClassPathResource("spring-config.xml");
        BeanFactory bf = new DefaultListableBeanFactory();
        BeanDefinitionReader bdr = new XmlBeanDefinitionReader((BeanDefinitionRegistry) bf);
        bdr.loadBeanDefinitions(resource);
        MyBean myBean = (MyBean) bf.getBean("myTestBean");
        System.out.println(myBean.getTestStr());
    }
}
  • 结果

控制台打印了testStr

上面代码做了哪些事:

  1. 读取spring-config.xml配置文件。
  2. 创建生产bean的工厂类BeanFactory
  3. BeanFactory根据配置文件中内容创建bean。

读取spring-config.xml配置文件

Resource类

配置文件的作用是记录bean信息,以及创建bean时的一些逻辑。所以Spring必然会有一些代码是用来处理配置文件的。因为配置文件的来源可能有很多种方式,例如:引用资源(在web项目resource根目录)classpath:spring-config.xml(类的加载路径)file:C:/src/spring-config.xml(文件路径)等。

Spring将配置文件的一些属性封装为Resource,包括:文件是否存在、是否可读、是否是Open状态、是否是文件;以及转化为URL、URI、File的方法;还有一些文件属性的获取。

URL、URI、File分别是Java中对url、uri、文件对应的对象。(Java将不同来源的资源抽象成URL,通过注册不同的URLStreamHandler来处理不同来源的资源读取逻辑,handler类型的选择根据不同的前缀(协议,Protocol)来识别)

Resource源码:(点击放大)
Spring源码1 配置文件的读取_第1张图片
InputStreamSource源码:(点击放大)上面Resource类继承了InputStreamSource接口,InputStreamSource源码如下:

Spring源码1 配置文件的读取_第2张图片

可以通过下面这种方式获取InputStream对象

Resource resource = new ClassPathResource("spring-config.xml");
InputStream in = resource.getInputStream();

Document类

获取到配置资源后,Spring将读取配置文件,并将spring-config.xml文件中的内容封装为Document类。在前面的实例中,这个过程在bdr.loadBeanDefinitions(resource);方法中体现。

深入该方法后,走到XmlBeanDefinitionReader类,loadBeanDefinitions(Resource resource)方法如下。

可以看到loadBeanDefinitions中调用了该类下的同名方法,调用方法前将Resource封装为EncodedResource类,可以推断这个方法是用来给文件做编码处理的,该类中的主要逻辑体现在getReader()方法,当设置了编码属性的时候Spring会使用相应的编码作为输入流的编码。

loadBeanDefinitions(EncodedResource encodedResource)方法细节:(点击放大)
Spring源码1 配置文件的读取_第3张图片

该方法中最终返回的是doLoadBeanDefinitions(inputSource, encodedResource.getResource())执行的返回结果,doLoadBeanDefinitions方法中传入两个参数,其中第一个参数inputSource是Resource对应的InputStream封装得到,目的是想要通过SAX读取XML文件的方式来准备InputSource对象。真正处理的方法是doLoadBeanDefinitions(inputSource, encodedResource.getResource())方法。

doLoadBeanDefinitions(inputSource, encodedResource.getResource())方法细节:(点击放大)
Spring源码1 配置文件的读取_第4张图片

到了这个方法后,终于见到了Document类。继续跟踪doLoadDocument方法。

下面分别解释this.documentLoader.loadDocument方法中的参数:

  • inputSource
  • getEntityResolver():得到一个解析器(org.xml.sax.EntityResolver)。(官网解释:如果SAX应用程序需要实现自定义处理外部实体,则必须实现此接口,并使用setEntityResolver方法向SAX 驱动器注册一个实例.也就是说,对于解析一个xml,sax首先会读取该xml文档上的声明,根据声明去寻找相应的dtd定义,以便对文档的进行验证,默认的寻找规则,(即:通过网络,实现上就是声明DTD的地址URI地址来下载DTD声明),并进行认证,下载的过程是一个漫长的过程,而且当网络不可用时,这里会报错,就是应为相应的dtd没找到。)该方法的存在就是提供了一个寻找DTD的方法,例如,可以指定本地,不必从网络加载。
  • this.errorHandler:xml错误处理。
  • getValidationModeForResource(resource):获取xml文件的验证模式。
  • isNamespaceAware():如果没有验证文件的话,namespaceAware必须设置而为true.(Set whether or not the XML parser should be XML namespace aware. Default is “false”. This is typically not needed when schema validation is active. However, without validation, this has to be switched to “true” in order to properly process schema namespaces.)

this.documentLoader.loadDocument方法主要做的就是将xml文件封装为Document对象,细节都是解析的过程。

下面回到doLoadDocument这里,继续看registerBeanDefinitions方法:该方法主要用来提取bean和注册bean。

Document doc = doLoadDocument(inputSource, resource);
return registerBeanDefinitions(doc, resource);

浅谈BeanDefinition注册

registerBeanDefinitions(doc, resource)方法:
在这里插入图片描述
同名方法documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
Spring源码1 配置文件的读取_第5张图片
继续跟踪doRegisterBeanDefinitions(root)方法:
Spring源码1 配置文件的读取_第6张图片
preProcessXml(root)postProcessXml(root)方法是空方法,这两个方法是为子类设计的,这是模板设计模式,如果继承DefaultBeanDefinitionDocumentReader的子类需要在Bean解析前后做一些处理的话,那么只需要重写这两个方法就可以了。

处理完profile标签以后就要进行xml文件的读取了,进入parseBeanDefinitions(root, this.delegate)方法中:
Spring源码1 配置文件的读取_第7张图片

Spring的xml配置里面有两大类Bean声明,一个是默认标签,例如:,另外一种是自定义标签,例如:

在介绍Spring默认标签解析之前,先介绍一下Spring中常用的一些标签。后面再介绍关于这些标签的解析。

你可能感兴趣的:(spring_v2,spring,resource,配置文件,源码)