• 容器的基础 XmlBeanFactory(下篇)


    前言

      这篇文章是接着上篇文章写的,如果没有看过上篇文章容器的基础 XmlBeanFactory(上篇)建议先去看一下,上篇文章讲述了加载bean之前的,Spring所要做的准备工作,这篇文章就直接讲述Spring加载bean的过程,话不多说,开始。

    加载Bean

      之前在上一篇文章中讲述到了在XmlBeanFactory构造函数中调用了XMLBeanDefinitionReader类型的reader属性提供的方法:this.reader.loadBeanDefinitions(resource),而这句代码则是整个资源加载的切入点,先来看看这个方法的时序图:

       从上面时序图中可以看出,加载XML文档和解析注册Bean,一直都是在做准备工作。根据上面的时序图我们来分析一下这里究竟在准备上面?

      (1)封装资源文件。当进入XMLBeanDefinitionReader后,首先对参数Resource使用EncodeResource类进行封装。

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

      (3)通过构造的InputSource实例和Resource实例继续调用函数doLoadBeanDefinitions。我们来看一下loadBeanDefinitions函数的具体实现过程:

      public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
            return loadBeanDefinitions(new EncodedResource(resource));
        }

       那么EncodedResource的作用是什么呢?通过名称,大致上可以推断出这个类的主要用于对资源文件的编码进行处理的。其中最主要的逻辑体现在getReader()方法中,当设置了编码属性的时候Spring会使用相应的编码作为输入流的编码。来看一下getReader()方法的源码:

      public Reader getReader() throws IOException {
            if (this.charset != null) {
                return new InputStreamReader(this.resource.getInputStream(), this.charset);
            }
            else if (this.encoding != null) {
                return new InputStreamReader(this.resource.getInputStream(), this.encoding);
            }
            else {
                return new InputStreamReader(this.resource.getInputStream());
            }
        }

       上面的代码构造了一个有编码(encoding)的InputStreamReader。当构造好的encodeResource对象后,再次转入了可复用方法loadBeanDefinitions(new EncodedResource(resource))。这个方法内部才是真正的数据准备阶段,也就是时序图描述的逻辑,一起来看一下:

     1 public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
     2         Assert.notNull(encodedResource, "EncodedResource must not be null");
     3         if (logger.isTraceEnabled()) {
     4             logger.trace("Loading XML bean definitions from " + encodedResource);
     5         }
     6         //通过属性来记录已经加载的资源
     7         Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
     8         if (currentResources == null) {
     9             currentResources = new HashSet<>(4);
    10             this.resourcesCurrentlyBeingLoaded.set(currentResources);
    11         }
    12         if (!currentResources.add(encodedResource)) {
    13             throw new BeanDefinitionStoreException(
    14                     "Detected cyclic loading of " + encodedResource + " - check your import definitions!");
    15         }
    16         try {
    17             //从encodeResource中获取已经封装的Resource资源,并从该资源中获取其中的InputStream
    18             InputStream inputStream = encodedResource.getResource().getInputStream();
    19             try {
    20                 InputSource inputSource = new InputSource(inputStream);
    21                 if (encodedResource.getEncoding() != null) {
    22                     inputSource.setEncoding(encodedResource.getEncoding());
    23                 }
    24                 return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
    25             }
    26             finally {
    27                 //关闭输入流
    28                 inputStream.close();
    29             }
    30         }
    31         catch (IOException ex) {
    32             throw new BeanDefinitionStoreException(
    33                     "IOException parsing XML document from " + encodedResource.getResource(), ex);
    34         }
    35         finally {
    36             currentResources.remove(encodedResource);
    37             if (currentResources.isEmpty()) {
    38                 this.resourcesCurrentlyBeingLoaded.remove();
    39             }
    40         }
    41     }

       SAX:SAX基于事件的解析,解析器在一次读取XML文件中根据读取的数据产生相应的事件,由应用程序实现相应的事件处理逻辑,即它是一种“推”的解析方式;这种解析方法速度快、占用内存少,但是它需要应用程序自己处理解析器的状态,实现起来会比较麻烦。

      再一次来梳理一下数据准备阶段的逻辑,首先对传入的Resource参数进行封装,目的是考虑到Resource可能存在编码要求的情况,其次,通过SAX读取XML文件的方式来准备InputSource对象,最后将准备的数据通过参数传递给真正处理的核心部分doLoadBeanDefinitions(inputSource,encodedREsource.getResource())。来了解一下这个方法:

     1 protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
     2             throws BeanDefinitionStoreException {
     3 
     4         try {
     5             Document doc = doLoadDocument(inputSource, resource);
     6             int count = registerBeanDefinitions(doc, resource);
     7             if (logger.isDebugEnabled()) {
     8                 logger.debug("Loaded " + count + " bean definitions from " + resource);
     9             }
    10             return count;
    11         }
    12         catch (BeanDefinitionStoreException ex) {
    13             throw ex;
    14         }
    15         catch (SAXParseException ex) {
    16             throw new XmlBeanDefinitionStoreException(resource.getDescription(),
    17                     "Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);
    18         }
    19         catch (SAXException ex) {
    20             throw new XmlBeanDefinitionStoreException(resource.getDescription(),
    21                     "XML document from " + resource + " is invalid", ex);
    22         }
    23         catch (ParserConfigurationException ex) {
    24             throw new BeanDefinitionStoreException(resource.getDescription(),
    25                     "Parser configuration exception parsing XML from " + resource, ex);
    26         }
    27         catch (IOException ex) {
    28             throw new BeanDefinitionStoreException(resource.getDescription(),
    29                     "IOException parsing XML document from " + resource, ex);
    30         }
    31         catch (Throwable ex) {
    32             throw new BeanDefinitionStoreException(resource.getDescription(),
    33                     "Unexpected exception parsing XML document from " + resource, ex);
    34         }
    35     }
    protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
            return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
                    getValidationModeForResource(resource), isNamespaceAware());
        }

      在上面的代码中,假如不考虑异常类的代码,其实只是做了三件事,这三件事的每一件都必不可少:

      (1)获取对XML文件的验证模式(在doLoadDocument方法里调用getValidationModeForResource(resource)方法)。

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

      (3)根据返回的Document注册Bean信息。

      这三个步骤支撑着整个Spring容器部分的实现基础,尤其是第三步对配置文件的解析,逻辑非常复杂。

    至此,整个Spring的容器基础,已经讲述完毕。

    参考:《Spring源码深度解析》 郝佳 编著:

    作者:Joe
    努力了的才叫梦想,不努力的就是空想,努力并且坚持下去,毕竟这是我相信的力量
  • 相关阅读:
    从csdn转移到博客园的一篇测试文章
    接口与抽象类的区别
    python网络爬虫进阶之HTTP原理,爬虫的基本原理,Cookies和代理介绍
    python验证码识别(2)极验滑动验证码识别
    VMWare虚拟机应用介绍
    Rpg maker mv角色扮演游戏制作大师简介
    python数据挖掘之数据探索第一篇
    python数据分析&挖掘,机器学习环境配置
    python爬取豆瓣视频信息代码
    python验证码处理(1)
  • 原文地址:https://www.cnblogs.com/Joe-Go/p/10057391.html
Copyright © 2020-2023  润新知