Spring源码学习(一)

  • A+
所属分类:Spring

买了《Spring源码深度解析》这本书,学习过程中记录总结一下。

首先看下面这段代码:

BeanFactory bf = new XmlBeanFactory(new ClassPathResource("beanFactoryTest.xml"));

Spring里是怎么实现的呢?先看下面的时序图

Spring源码学习(一)

因为初始化XmlBeanFactory需要参数resource,所以用Classpathresource来得到resource。然后调用XmlBeanDefinitionReader类的方法loadBeanDefinitions来加载Bean。看下面的时序图:

Spring源码学习(一)

这张图就比较复杂了,简单来说,只有resource是不够的,因为存在encode即编码格式的问题,所以需要得到EncodedResource对象。而doLoadedBeanDefinitions函数需要InputSource参数,InputSource不是Spring里的类,是java自带的类

org.xml.sax.InputSource。
/**
   * Load bean definitions from the specified XML file.
   * @param resource the resource descriptor for the XML file
   * @return the number of bean definitions found
   * @throws BeanDefinitionStoreException in case of loading or parsing errors
   */
  @Override
  public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
    return loadBeanDefinitions(new EncodedResource(resource));
  }

  /**
   * Load bean definitions from the specified XML file.
   * @param encodedResource the resource descriptor for the XML file,
   * allowing to specify an encoding to use for parsing the file
   * @return the number of bean definitions found
   * @throws BeanDefinitionStoreException in case of loading or parsing errors
   */
  public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
    Assert.notNull(encodedResource, "EncodedResource must not be null");
    if (logger.isInfoEnabled()) {
      logger.info("Loading XML bean definitions from " + encodedResource.getResource());
    }

    Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
    if (currentResources == null) {
      currentResources = new HashSet<>(4);
      this.resourcesCurrentlyBeingLoaded.set(currentResources);
    }
    if (!currentResources.add(encodedResource)) {
      throw new BeanDefinitionStoreException(
          "Detected cyclic loading of " + encodedResource + " - check your import definitions!");
    }
    try {
      InputStream inputStream = encodedResource.getResource().getInputStream();
      try {
        InputSource inputSource = new InputSource(inputStream);
        if (encodedResource.getEncoding() != null) {
          inputSource.setEncoding(encodedResource.getEncoding());
        }
        return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
      }
      finally {
        inputStream.close();
      }
    }
    catch (IOException ex) {
      throw new BeanDefinitionStoreException(
          "IOException parsing XML document from " + encodedResource.getResource(), ex);
    }
    finally {
      currentResources.remove(encodedResource);
      if (currentResources.isEmpty()) {
        this.resourcesCurrentlyBeingLoaded.remove();
      }
    }
  }

 

可以看出整个的核心部分就是doLoadedBeanDefinitions函数。看下这个函数的源码。可以看出这个函数做了两件事,一个是得到Document对象,二是根据Document对象来注册Bean。而看这里才猛然发现因为要得到Document对象所以之前才需要得到InputSource对象。

/**
   * Actually load bean definitions from the specified XML file.
   * @param inputSource the SAX InputSource to read from
   * @param resource the resource descriptor for the XML file
   * @return the number of bean definitions found
   * @throws BeanDefinitionStoreException in case of loading or parsing errors
   * @see #doLoadDocument
   * @see #registerBeanDefinitions
   */
  protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
      throws BeanDefinitionStoreException {
    try {
      Document doc = doLoadDocument(inputSource, resource);
      return registerBeanDefinitions(doc, resource);
    }
    catch (BeanDefinitionStoreException ex) {
      throw ex;
    }
    catch (SAXParseException ex) {
      throw new XmlBeanDefinitionStoreException(resource.getDescription(),
          "Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);
    }
    catch (SAXException ex) {
      throw new XmlBeanDefinitionStoreException(resource.getDescription(),
          "XML document from " + resource + " is invalid", ex);
    }
    catch (ParserConfigurationException ex) {
      throw new BeanDefinitionStoreException(resource.getDescription(),
          "Parser configuration exception parsing XML from " + resource, ex);
    }
    catch (IOException ex) {
      throw new BeanDefinitionStoreException(resource.getDescription(),
          "IOException parsing XML document from " + resource, ex);
    }
    catch (Throwable ex) {
      throw new BeanDefinitionStoreException(resource.getDescription(),
          "Unexpected exception parsing XML document from " + resource, ex);
    }
  }

 

上面的代码是Spring5.0里的代码,《Spring源码深度解析》书里的代码多了一个做xml的验证的部分。应该是老版本才有的。

/**
 * Register the bean definitions contained in the given DOM document.
 * Called by {@code loadBeanDefinitions}.
 * <p>Creates a new instance of the parser class and invokes
 * {@code registerBeanDefinitions} on it.
 * @param doc the DOM document
 * @param resource the resource descriptor (for context information)
 * @return the number of bean definitions found
 * @throws BeanDefinitionStoreException in case of parsing errors
 * @see #loadBeanDefinitions
 * @see #setDocumentReaderClass
 * @see BeanDefinitionDocumentReader#registerBeanDefinitions
 */
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
  BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
  int countBefore = getRegistry().getBeanDefinitionCount();
  documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
  return getRegistry().getBeanDefinitionCount() - countBefore;
}

/**
 * Create the {@link BeanDefinitionDocumentReader} to use for actually
 * reading bean definitions from an XML document.
 * <p>The default implementation instantiates the specified "documentReaderClass".
 * @see #setDocumentReaderClass
 */
protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() {
  return BeanDefinitionDocumentReader.class.cast(BeanUtils.instantiateClass(this.documentReaderClass));
}

上面这两段内容没什么特别的,但是BeanDefinitionDocumentReader也是一个接口。具体registerBeanDefinitions的实现在DefaultBeanDefinitionDocumentReader里。。因为this.documentReaderClass的类型是DefaultBeanDefinitionDocumentReader。

  /**
   * This implementation parses bean definitions according to the "spring-beans" XSD
   * (or DTD, historically).
   * <p>Opens a DOM Document; then initializes the default settings
   * specified at the {@code <beans/>} level; then parses the contained bean definitions.
   */
  @Override
  public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
    this.readerContext = readerContext;
    logger.debug("Loading bean definitions");
    Element root = doc.getDocumentElement();
    doRegisterBeanDefinitions(root);
  }

/**
   * Register each bean definition within the given root {@code <beans/>} element.
   */
protected void doRegisterBeanDefinitions(Element root) {
    // Any nested <beans> elements will cause recursion in this method. In
    // order to propagate and preserve <beans> default-* attributes correctly,
    // keep track of the current (parent) delegate, which may be null. Create
    // the new (child) delegate with a reference to the parent for fallback purposes,
    // then ultimately reset this.delegate back to its original (parent) reference.
    // this behavior emulates a stack of delegates without actually necessitating one.
    BeanDefinitionParserDelegate parent = this.delegate;
    this.delegate = createDelegate(getReaderContext(), root, parent);

    if (this.delegate.isDefaultNamespace(root)) {
      String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
      if (StringUtils.hasText(profileSpec)) {
        String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
            profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
        if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
          if (logger.isInfoEnabled()) {
            logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec +
                "] not matching: " + getReaderContext().getResource());
          }
          return;
        }
      }
    }

    preProcessXml(root);
    parseBeanDefinitions(root, this.delegate);
    postProcessXml(root);

    this.delegate = parent;
  }

可以看到真正实现的方法是parseBeanDefinitions(root, this.delegate);

/**
   * Parse the elements at the root level in the document:
   * "import", "alias", "bean".
   * @param root the DOM root element of the document
   */
  protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
    if (delegate.isDefaultNamespace(root)) {
      NodeList nl = root.getChildNodes();
      for (int i = 0; i < nl.getLength(); i++) {
        Node node = nl.item(i);
        if (node instanceof Element) {
          Element ele = (Element) node;
          if (delegate.isDefaultNamespace(ele)) {
            parseDefaultElement(ele, delegate);
          }
          else {
            delegate.parseCustomElement(ele);
          }
        }
      }
    }
    else {
      delegate.parseCustomElement(root);
    }
  }

这一段里判断了是否是默认命名空间,如果是就解析默认标签,如果不是,则解析自定义标签.看了代码后发现其实判断是否是默认命名空间其实就是与字符串

"http://www.springframework.org/schema/beans"来比较。相同就是默认命名空间。
ZPY

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: