【有书共读】《Spring源码深度解读》第二章

第二章 容器的基本实现

笔记有github链接,用电脑看笔记效果更佳。

容器的基本用法:

        BeanSpring中最核心的东西,因为Spring就像是个大水桶,而bean就像容器里的水,水桶脱离了水,也就没什么用处了,所以此章主要就是讲bean的解析及注册。

        Bean没有什么特别之处,Spring的目的就是让我们的bean能成为一个纯粹的pojo,这也是Spring所追求的。接下来看看配置文件。
        

    在上面我们看到了bean的声明方式,尽管springbean的元素定义着N种属性来支撑我们业务的各种应用,但是只要我们声明成这样,基本上已经可以满足我们大多数的应用了。

    类:

        

    接下来写一下测试代码:

    @SuppressWarnings("deprecation")

    public class BeanFactoryTest {

       public void testSimpleLoad(){

                   BeanFactorybf = new XmlBeanFactory(new ClassPathResource("beanFactoryTest.xml"));

                   MyTestBean bean = (MyTestBean)bf.getBean("myTestBean");

                   assertEquals("testStr", bean.getTestStr());

         }

 

    }

    期望的结果:在Eclipse中显示Green Bar

    直接使用BeanFactory作为容器对Spring的使用来说不多见,甚至很少,企业中大多数使用ApplicationContext

功能分析:

    上面的测试代码究竟做了什么工作?无非一下几点:

    (1)读取配置文件beanFactory.xml

    (2)根据beanFactory.xml中的配置找到对应的类的配置,并实例化。

    (3)调用实例化后的实例。

    为了更清楚的描述,画一个类图。

    

        ConfigReader:用于读取及验证配置文件。我们要用配置文件里边的东西,首先就是读取,然后放置在内存中。

        ReflectionUtil:用于根据配置文件中的配置进行反射实例化。

        App:用于完成整个逻辑的串联。

容器的基础XmlBeanFactory:

    测试代码中:

    BeanFactorybf = new XmlBeanFactory(new ClassPathResource("beanFactoryTest.xml"));

    首先调用ClassPathResource的构造函数来构造Resource资源文件的实例对象,这样后续就可以用Resource提供的各种服务来操作了,有了Resource后就可以进行XmlBeanFactory的初始化了。(Resource接口可以对所有的资源文件进行统一处理)

    Resource接口封装底层资源的过程:https://github.com/spring-projects/spring-framework/blob/616a40adb64a09dc4caeee67f16aabb188eeae7a/spring-core/src/main/java/org/springframework/core/io/Resource.java

    当通过Resource相关类完成了对配置文件进行封装,配置文件的读取工作就全权交给XmlBeanDefinitionReader来处理。

    了解了Spring中配置文件为Resource类型的实例方法后,可以继续探寻XmlBeanFactory的初始化过程,这里使用Resource实例作为构造函数参数的办法。

    代码:

    Public XmlBeanFactoryResource resourcethrows BeanException{

    This(resource, null);

    }

    Public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws Beanexception{

    Super(parentBeanFactory);

    This.reader.loadBeanDefinitions(resource);

    }

    This.reader.loadBeanDefinitions(resource);才是资源加载的真正实现,绕了半天还没有切入正题,但是前边的资源封装工作也是非常必要的。

    loadBeanDefinitions(resource)方法都做了什么?

    (1)封住资源文件。当进入XmlBeanDefinitionReader后首先对参数Resource使用EncodeResource(对资源文件的编码进行处理)类进行封装。

    (2)获取输入流。从Resource中获取对应的InputStream并构造InputSource

    (3)通过InputSource实例和Resource实例继续调用函数doLoadBeanDefinitions

    loadBeanDefinitiontions方法链接:https://github.com/spring-projects/spring-framework/blob/a89e716cc71a8741385a0224b5d7eb7ce009e11a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanDefinitionReader.java

    再次梳理一下数据准备阶段的逻辑,首先对传入的Resource参数做封装,目的是考虑到Resource可能存在编码要求的情况,其实通过SAX读取XML文件的方式来准备InputSource对象,最后将准备的数据传入真正的狠心处理部分doLoadBeanDefinitions

    doLoadBeanDefinitions方法:


    

    在上两段代码中,其实只做了三件事:

    (1)获取对XML文件的验证模式。

    (2)加载XML文件,并得到对应的Document

    (3)根据Document注册bean信息。

    这三个步骤支撑着整个Spring容器部分的实现基础,尤其是第三部对配置文件的解析,逻辑非常复杂,那么,就从获取XML文件的验证模式开始。

获取XML的验证模式:

    XML文件的验证模式保证了XML文件的正确性,而比较常用的模式有两种:DTDXSD

    DTD(Document Type Definition)即文档性定义,DTD是一种保证XML文档格式正确的有效方法,可以通过比较XML文档和DTD文件来看文档是否符合规范,元素和标签是否使用正确。一个DTD文档包含:元素的定义规则,元素间关系的定义规则,元素可使用的属性,可使用的实体或符号规则。

    XML Schema语言就是XSDXML Schemas Definition)。XML Schema描述了xML文档的结构,可以用一个指定的XML Schema来雁阵给一个XML文档,以检查XML文档是否符合要求。在使用XML Schema文档对XML实例文档进行检验,除了要声明名称空间外,还必须指定该名称空间多对应的XML     Schema文档的存储位置。包含两个部分,一部分是名称空间URI,另一部分就是该名称空间所标识的XML Schema文件位置或URL地址。

    了解了DTDXSD之后,看源码。

    Spring通过getValidationModeForResource方法来获取对应资源的验证模式。

    

    而自动检测验证模式的功能是在函数detectValidationMode方法中实现的

    
    再此又调用了
    

    Spring用来检验验证模式的办法就是判断是否包含DOCTYPE,如果包含就是DTD,否则就是XSD

获取Document:

    经过了验证模式准备的步骤就可以进行Document加载了,DocumentLoader去执行,DocumentLoader只是接口,而真正调用的是DefaultDocumentLoader

    

    通过SAX解析xML文档的套路大概差不多,首先创建DocumentBuilderFactory,再通过DocumentBuilderFactory创建DocumentBuilder,进而解析inputSource来返回Document对象。

    对于传入参数EntityResolver,是什么?

    官网这样解释:如果SAX程序要实现自定义处理外部实体,必须实现此接口并使用setEntityResolver方法向SAX驱动器注册一个实例。

    EntityResolver的作用是项目本身就可以提供一个如何寻找DTD声明的方法,即由程序来实现寻找DTD声明的过程。

    Spring中使用DelegatingEntityResolver类为EntityResolver的实现类,resolveEntity为实现方法。(publicID, systemID根据配置文件得到,此方法返回inputSource对象)

    

    对于不同的加载模式Spring使用了不同解析器解析。最终得到了Document,现在要开始重头戏了,提取以及注册bean

解析及注册BeanDefinitions:
    

    这个方法中很好的运用了面向对象中的单一职责的原则,将逻辑处理委托给单一的类进行处理,而这个逻辑处理类就是BeanDefinitionDocumentReaderBeanDefinitionDocumentReader是一个接口,实例化工作则是在createBeanDefinitionDocumentReader()中完成,这个方法的重要目的之一就是提取root,以便于再次将root作为参数继续BeanDefinition的注册。

    Public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext){

    This.readerContext = readContext();

    Logger.debug(“loading bean definitions”);

    Element root = doc.getDocumentElement();

    doRegisterBeanDefinitions(root);

    }

    终于到了核心逻辑的底部doRegisterBeanDefinitions(root)之前都是在做准备工作。

    

    截止目前,解析工作将要真正的开始了,具体内容一下笔记再见!

注: 另外一点就是:Spring对于实现某一块,并不是直接实现,是通过很多调用,和很多验证,
层层嵌套,直到do开头的方法中才会看到真正的实现逻辑。如registerBeanDefinitions,
registerBeanDefinitions这个方法内并没有真正的实现,只是进行一些操作,而真正的实
现是在doRegisterBeanDefinitions,其他的也是如此,如createBean,真正的实现逻辑是
在doCreateBean……


全部评论

相关推荐

点赞 评论 收藏
分享
点赞 收藏 评论
分享
牛客网
牛客企业服务