spring源碼分析2----讀xml中的bean

作者:shihuaping0918@163.com 轉(zhuǎn)載請注明作者

上一篇講到XmlBeanDefinitionReader讀xml文件,這一篇繼續(xù)。在正式開始之前,我們先回顧一下bean在xml配置中的格式是什么樣子,這里面有關(guān)鍵字要先說明一下:

<?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>

其中要關(guān)注的關(guān)鍵字就是beans和bean。下面繼續(xù)上一篇講到的方法loadBeanDefinitions,它實際上是被另一個同名方法調(diào)用的,代碼如下:

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是臨時創(chuàng)建的,這也意味著,方法調(diào)用結(jié)束,它就會被GC了。其中傳入了一個beanFactory參數(shù)。先看一下XmlBeanDefinitionReader做了什么,文章末尾再看是誰調(diào)用了loadBeanDefinitions(DefaultListableBeanFactory beanFactory)。

XmlBeanDefinitionReader位于這個包下面。

package org.springframework.beans.factory.xml;

前面調(diào)用的方法代碼如下

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

從代碼看,任務(wù)轉(zhuǎn)交給了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。我們繼續(xù)跟蹤下去,注意到了這里,已經(jīng)是跳到另一個類了,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;
}

代碼中創(chuàng)建了一個代理,delegate,先標記一下。這里面完成功能的是這三行:

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

第二行是解析xml樹狀結(jié)構(gòu),使用到了代理類。這個代理我們先略過,它是處理xml樹狀結(jié)構(gòu)的,相當于語義分析,不是說它沒有用,只是因為篇幅原因,沒辦法展開去討論分析了。文章一開始介紹了xml中bean的配置格式,提到要注意beans關(guān)鍵字。這個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是對應(yīng)這兩個常量,那么需要關(guān)注的兩個方法就是processBeanDefinition和doRegisterBeanDifinitions。忽略doRegisterBeanDifinitions,因為這個最終要回來調(diào)processBeanDefinition方法的,現(xiàn)在就只關(guān)注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()注冊了。整個過程,很長,不直觀,跳轉(zhuǎn)了幾個類?,F(xiàn)在還有一個沒完成的,就是bean注冊到哪里去了。第一篇提到的refresh方法又是怎么調(diào)用方法去讀取xml文件的。refresh方法留到下一篇再講,這一篇先聚焦在bean的加載,注冊。

從代碼回溯來看,注冊目的地是這里。那么這個registry到底是什么?

DefaultBeanDefinitionDocumentReader
getReaderContext().getRegistry()

現(xiàn)在反向?qū)ふ宜膩碓?,先找?/p>

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

繼續(xù)反向追蹤,發(fā)現(xiàn)是在這里設(shè)置的:

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

readerContext是通過參數(shù)傳進來的。再往回看看,退回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());  //注意這些參數(shù)
}

好,現(xiàn)在來看XmlReaderContext這個類的構(gòu)造方法:

public XmlReaderContext(
      Resource resource, ProblemReporter problemReporter,
      ReaderEventListener eventListener, SourceExtractor sourceExtractor,
      XmlBeanDefinitionReader reader, NamespaceHandlerResolver namespaceHandlerResolver) {
//注意reader是倒數(shù)第二個參數(shù)
   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類的實例。挖了半天,終于挖到實質(zhì)內(nèi)容了?;仡^看XmlBeanDefinitionReader,這個類里沒有實現(xiàn)getRegistry這方法,看一下它的老父親AbstractBeanDefinitionReader。這個老父親有兩個方法,暴露了真相。

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

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

但很坑的是,這又是一個成員。還要再追蹤這個成員是什么,是哪來的。再跳回XmlBeanDefinitionReader,有這么一個構(gòu)造方法:

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

好了,這個registry是外面?zhèn)鬟M來的,現(xiàn)在需要返回到創(chuàng)建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,又是傳進來的參數(shù)。到了這里,可能讀者都想吐了,但是我們不要放棄,勝利就在前方了。找一下,是誰在調(diào)loadBeanDefinitions。答案就是它,這個類里有一個方法在調(diào)用。

AbstractRefreshableApplicationContext

事實勝于熊的大便,繼續(xù)貼代碼。

@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這部分代碼的作者沒有走心,他是抱著完成任務(wù)的心態(tài)在寫,一個類廠傳了那么多次。

在DefaultListableBeanFactory,會將類名指向的bean加載到虛擬機里。

寫在最后:接單,有后臺活java/cpp/lua/go聯(lián)系shihuaping0918@163.com。不上班了也要有點收入才行。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容