美文网首页
spring源码分析2----读xml中的bean

spring源码分析2----读xml中的bean

作者: 天一阁图书管理员 | 来源:发表于2020-02-17 17:11 被阅读0次

    作者:shihuaping0918@163.com 转载请注明作者

    上一篇讲到XmlBeanDefinitionReader读xml文件,这一篇继续。在正式开始之前,我们先回顾一下bean在xml配置中的格式是什么样子,这里面有关键字要先说明一下:

    <?xml version="1.0" encoding="UTF-8"?>
    <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 definitions here -->
        <bean id="myField" class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean">
            <property name="staticField" value="java.sql.Connection.TRANSACTION_SERIALIZABLE"/>
        </bean>
    </beans>
    

    其中要关注的关键字就是beans和bean。下面继续上一篇讲到的方法loadBeanDefinitions,它实际上是被另一个同名方法调用的,代码如下:

    protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
       // Create a new XmlBeanDefinitionReader for the given BeanFactory.
       XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
    
       // Configure the bean definition reader with this context's
       // resource loading environment.
       beanDefinitionReader.setEnvironment(this.getEnvironment());
       beanDefinitionReader.setResourceLoader(this);
       beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
    
       // Allow a subclass to provide custom initialization of the reader,
       // then proceed with actually loading the bean definitions.
       initBeanDefinitionReader(beanDefinitionReader);
       loadBeanDefinitions(beanDefinitionReader);
    }
    

    从代码可以看出来,XmlBeanDefinitionReader是临时创建的,这也意味着,方法调用结束,它就会被GC了。其中传入了一个beanFactory参数。先看一下XmlBeanDefinitionReader做了什么,文章末尾再看是谁调用了loadBeanDefinitions(DefaultListableBeanFactory beanFactory)。

    XmlBeanDefinitionReader位于这个包下面。

    package org.springframework.beans.factory.xml;
    

    前面调用的方法代码如下

    public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
       Assert.notNull(encodedResource, "EncodedResource must not be null");
       if (logger.isTraceEnabled()) {
          logger.trace("Loading XML bean definitions from " + encodedResource);
       }
    
       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();
          }
       }
    }
    

    从代码看,任务转交给了doLoadBeanDefinitions。

    protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
          throws BeanDefinitionStoreException {
    
       try {
          Document doc = doLoadDocument(inputSource, resource);
          int count = registerBeanDefinitions(doc, resource);
          if (logger.isDebugEnabled()) {
             logger.debug("Loaded " + count + " bean definitions from " + resource);
          }
          return count;
       }
       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);
       }
    }
    

    注意这两行

    Document doc = doLoadDocument(inputSource, resource);
    int count = registerBeanDefinitions(doc, resource);
    

    上一行读取了xml文件,下一行将xml文件中的bean注册到了beanfactory里面。

    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;
    }
    

    从这段代码可以看出,负责注册的是BeanDefinitionDocumentReader。这个类最终来源于

    private Class<? extends BeanDefinitionDocumentReader> documentReaderClass =
          DefaultBeanDefinitionDocumentReader.class;
    

    也就是DefaultBeanDefinitionDocumentReader。我们继续跟踪下去,注意到了这里,已经是跳到另一个类了,DefaultBeanDefinitionDocumentReader。

    @Override
    public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
       this.readerContext = readerContext;
       doRegisterBeanDefinitions(doc.getDocumentElement());
    }
    

    再跟doRegisterBeanDefinitions

    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);
             // We cannot use Profiles.of(...) since profile expressions are not supported
             // in XML config. See SPR-12458 for details.
             if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
                if (logger.isDebugEnabled()) {
                   logger.debug("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;
    }
    

    代码中创建了一个代理,delegate,先标记一下。这里面完成功能的是这三行:

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

    第二行是解析xml树状结构,使用到了代理类。这个代理我们先略过,它是处理xml树状结构的,相当于语义分析,不是说它没有用,只是因为篇幅原因,没办法展开去讨论分析了。文章一开始介绍了xml中bean的配置格式,提到要注意beans关键字。这个beans就是下面的NESTED_BEANS_ELEMENT。而bean就是下面的BEAN_ELEMENT。

    private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
       if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
          importBeanDefinitionResource(ele);
       }
       else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
          processAliasRegistration(ele);
       }
       else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) { //xml中的bean
          processBeanDefinition(ele, delegate);
       }
       else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {  //xml中的beans
          // recurse
          doRegisterBeanDefinitions(ele);
       }
    }
    

    好,既然我们知道了beans和bean是对应这两个常量,那么需要关注的两个方法就是processBeanDefinition和doRegisterBeanDifinitions。忽略doRegisterBeanDifinitions,因为这个最终要回来调processBeanDefinition方法的,现在就只关注processBeanDefinition。

    protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
       BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
       if (bdHolder != null) {
          bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
          try {
             // Register the final decorated instance.
             BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
          }
          catch (BeanDefinitionStoreException ex) {
             getReaderContext().error("Failed to register bean definition with name '" +
                   bdHolder.getBeanName() + "'", ele, ex);
          }
          // Send registration event.
          getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
       }
    }
    

    前面提到的代理又跳出来了,忽略它,到这个方法为止,bean就被注册进去了。不放心可以再跟一下,这次是在类BeanDefinitionReaderUtils里面了:

    public static void registerBeanDefinition(
          BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
          throws BeanDefinitionStoreException {
    
       // Register bean definition under primary name.
       String beanName = definitionHolder.getBeanName();
       registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
    
       // Register aliases for bean name, if any.
       String[] aliases = definitionHolder.getAliases();
       if (aliases != null) {
          for (String alias : aliases) {
             registry.registerAlias(beanName, alias);
          }
       }
    }
    

    看到了,最终是registry.registerBeanDefinition()注册了。整个过程,很长,不直观,跳转了几个类。现在还有一个没完成的,就是bean注册到哪里去了。第一篇提到的refresh方法又是怎么调用方法去读取xml文件的。refresh方法留到下一篇再讲,这一篇先聚焦在bean的加载,注册。

    从代码回溯来看,注册目的地是这里。那么这个registry到底是什么?

    DefaultBeanDefinitionDocumentReader
    getReaderContext().getRegistry()
    

    现在反向寻找它的来源,先找到

    protected final XmlReaderContext getReaderContext() {
       Assert.state(this.readerContext != null, "No XmlReaderContext available");
       return this.readerContext;
    }
    

    继续反向追踪,发现是在这里设置的:

    @Override
    public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
       this.readerContext = readerContext;
       doRegisterBeanDefinitions(doc.getDocumentElement());
    }
    

    readerContext是通过参数传进来的。再往回看看,退回XmlBeanDefinitionReader这个类。

    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 XmlReaderContext} to pass over to the document reader.
     */
    public XmlReaderContext createReaderContext(Resource resource) {
       return new XmlReaderContext(resource, this.problemReporter, this.eventListener,
             this.sourceExtractor, this, getNamespaceHandlerResolver());  //注意这些参数
    }
    

    好,现在来看XmlReaderContext这个类的构造方法:

    public XmlReaderContext(
          Resource resource, ProblemReporter problemReporter,
          ReaderEventListener eventListener, SourceExtractor sourceExtractor,
          XmlBeanDefinitionReader reader, NamespaceHandlerResolver namespaceHandlerResolver) {
    //注意reader是倒数第二个参数
       super(resource, problemReporter, eventListener, sourceExtractor);
       this.reader = reader;
       this.namespaceHandlerResolver = namespaceHandlerResolver;
    }
    
    /**
     * Return the bean definition registry to use.
     * @see XmlBeanDefinitionReader#XmlBeanDefinitionReader(BeanDefinitionRegistry)
     */
    public final BeanDefinitionRegistry getRegistry() {
       return this.reader.getRegistry();  //注意这个reader
    }
    

    可以看出来,XmlReaderContext的reader成员,实际上就是XmlBeanDefinitionReader类的实例。挖了半天,终于挖到实质内容了。回头看XmlBeanDefinitionReader,这个类里没有实现getRegistry这方法,看一下它的老父亲AbstractBeanDefinitionReader。这个老父亲有两个方法,暴露了真相。

    public final BeanDefinitionRegistry getBeanFactory() {
       return this.registry;
    }
    
    @Override
    public final BeanDefinitionRegistry getRegistry() {
       return this.registry;
    }
    
    

    但很坑的是,这又是一个成员。还要再追踪这个成员是什么,是哪来的。再跳回XmlBeanDefinitionReader,有这么一个构造方法:

    public XmlBeanDefinitionReader(BeanDefinitionRegistry registry) {
       super(registry);
    }
    

    好了,这个registry是外面传进来的,现在需要返回到创建XmlBeanDefinitionReader的地方。这是AbstractXmlApplicationContext类中的方法。

    @Override
    protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
       // Create a new XmlBeanDefinitionReader for the given BeanFactory.
       XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
    
       // Configure the bean definition reader with this context's
       // resource loading environment.
       beanDefinitionReader.setEnvironment(this.getEnvironment());
       beanDefinitionReader.setResourceLoader(this);
       beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
    
       // Allow a subclass to provide custom initialization of the reader,
       // then proceed with actually loading the bean definitions.
       initBeanDefinitionReader(beanDefinitionReader);
       loadBeanDefinitions(beanDefinitionReader);
    }
    

    wtf,可以看到这个factory,又是传进来的参数。到了这里,可能读者都想吐了,但是我们不要放弃,胜利就在前方了。找一下,是谁在调loadBeanDefinitions。答案就是它,这个类里有一个方法在调用。

    AbstractRefreshableApplicationContext
    

    事实胜于熊的大便,继续贴代码。

    @Override
    protected final void refreshBeanFactory() throws BeansException {
       if (hasBeanFactory()) {
          destroyBeans();
          closeBeanFactory();
       }
       try {
          DefaultListableBeanFactory beanFactory = createBeanFactory(); //看这里
          beanFactory.setSerializationId(getId());
          customizeBeanFactory(beanFactory);
          loadBeanDefinitions(beanFactory);  //看这里
          synchronized (this.beanFactoryMonitor) {
             this.beanFactory = beanFactory;
          }
       }
       catch (IOException ex) {
          throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
       }
    }
    

    好,我们终于看到,bean是注册到了DefaultListableBeanFactory里面。这个类厂传递之广泛和深入,让我感觉spring这部分代码的作者没有走心,他是抱着完成任务的心态在写,一个类厂传了那么多次。

    在DefaultListableBeanFactory,会将类名指向的bean加载到虚拟机里。

    写在最后:接单,有后台活java/cpp/lua/go联系shihuaping0918@163.com。不上班了也要有点收入才行。

    相关文章

      网友评论

          本文标题:spring源码分析2----读xml中的bean

          本文链接:https://www.haomeiwen.com/subject/ouraactx.html