文章出處

我們的日常開發幾乎離不開Spring,他為我們的開發帶來了很大的便捷,那么Spring框架是如何做到方便他人的呢。今天就來說說bean如何被加載加載。

我們在xml文件中寫過太多類似這樣的bean聲明

<bean id="jsonParser" class="com.jackie.json.FastjsonJsonParser" />

那么這樣的聲明是如果被Spring讀取并加載的呢?

DefaultListableBeanFactory

繼承關系

public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
      implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable 

上面是DefaultListableBeanFactory類源碼的聲明,集成了父類AbstractAutowireCapableBeanFactory以及實現了接口
ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable(且不管這些又長有難懂的父類和父接口都是干什么的)

上面是DefaultListableBeanFactory的所有依賴父類,可以看到最頂層使我們熟悉的Object類。

地位
DefaultListableBeanFactory類是整個bean加載的核心部分。后面我們要說到的XmlBeanFactory就是DefaultListableBeanFactory的子類。

XmlBeanFactory

XmlBeanFactory是DefaultListableBeanFactory的子類。其與DefaultListableBeanFactory類的不同之處通過看源碼就會發現,XmlBeanFactory使用了自定義的XmlBeanDefinitionReader用于讀取xml文件內容。

XmlBeanFactory的源碼并不復雜

public class XmlBeanFactory extends DefaultListableBeanFactory {

   private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);


   /**
    * Create a new XmlBeanFactory with the given resource,
    * which must be parsable using DOM.
    * @param resource XML resource to load bean definitions from
    * @throws BeansException in case of loading or parsing errors
    */
   public XmlBeanFactory(Resource resource) throws BeansException {
      this(resource, null);
   }

   /**
    * Create a new XmlBeanFactory with the given input stream,
    * which must be parsable using DOM.
    * @param resource XML resource to load bean definitions from
    * @param parentBeanFactory parent bean factory
    * @throws BeansException in case of loading or parsing errors
    */
   public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
      super(parentBeanFactory);
      this.reader.loadBeanDefinitions(resource);
   }

}
  • 有兩個構造函數
  • 兩個構造函數都有參數Resource,我們在調用前可以通過各種實現比如new ClassPathResource("application-context.xml")的方式傳值
  • Resource有很多實現比如 文件(FileSystemResource)、URL資源(UrlResource)、InputStream資源(InputStreamResource)、Byte數組(ByteArrayResource)等
  • 使用Resource是因為該接口抽象出了Spring用到的各種底層資源比如File、Url、Classpath等

bean加載過程

說到這里的bean加載就離不開我們上面提到的XmlBeanFactory類。
一切的加載XML以及解析bean的操作都要從XmlBeanFactory的
XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory)
說起。

步驟分析

  • 理所當然,我們首先點擊進入XmlBeanDefinitionReader類的loadBeanDefinitions方法。整個過程簡單說就是:
    1.首先對傳入的Resouce進行封裝,成為EncodeResource對象(之所以這么做,是考慮可以對于資源文件進行編碼)。

    Assert.notNull(encodedResource, "EncodedResource must not be null");
        if (logger.isInfoEnabled()) {
            logger.info("Loading XML bean definitions from " + encodedResource.getResource());
        }
  1. 得到Resource的輸入流

    InputStream inputStream = encodedResource.getResource().getInputStream();

3.調用doLoadBeanDefinitions方法

  • 進入doLoadBeanDefinitions方法,我們可以看到方法實現也很簡單

    protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
            throws BeanDefinitionStoreException {
        try {
            int validationMode = getValidationModeForResource(resource);
            Document doc = this.documentLoader.loadDocument(
                    inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware());
            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);
        }
    }

主要做了三件事
得到XML文件的驗證模式

int validationMode = getValidationModeForResource(resource);

2.加載XML文件,得到對應的Document對象(這里可以發現是調用了this.documentLoader即DefaultDocmentLoader類)

Document doc = this.documentLoader.loadDocument(
      inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware());

這里的解析成Document其實就是常規的SAX解析XML的操作。

3.根據得到的Document對象注冊Bean

registerBeanDefinitions(doc, resource)

解析注冊BeanDefinitions

上面的在得到Docment對象后,就是需要解析注冊BeanDefinitions了。
進入registerBeanDefinitions方法我們可以看到其實現為

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
        BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
        documentReader.setEnvironment(this.getEnvironment());
        int countBefore = getRegistry().getBeanDefinitionCount();
        documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
        return getRegistry().getBeanDefinitionCount() - countBefore;
    }

我們繼續深入,則進入到類DefaultBeanDefinitionDocumentReaderregisterBeanDefinitions方法的doRegisterBeanDefinitions實現。
在這個方法里實現了對于XML中的bean真正的解析

protected void doRegisterBeanDefinitions(Element root) {
   String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
   if (StringUtils.hasText(profileSpec)) {
      Assert.state(this.environment != null, "Environment must be set for evaluating profiles");
      String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
            profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
      if (!this.environment.acceptsProfiles(specifiedProfiles)) {
         return;
      }
   }

   // 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 = createHelper(this.readerContext, root, parent);

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

   this.delegate = parent;
}

追蹤該方法中的每一個方法就是具體執行解析xml并加載bean的過程,有興趣可以打斷點調試一遍。

如果您覺得閱讀本文對您有幫助,請點一下“推薦”按鈕,您的“推薦”將是我最大的寫作動力!如果您想持續關注我的文章,請掃描二維碼,關注JackieZheng的微信公眾號,我會將我的文章推送給您,并和您一起分享我日常閱讀過的優質文章。


文章列表


不含病毒。www.avast.com
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()