IoC 容器的初始化之 BeanDefinition 的載入和解析

在上一篇文章图甜,我們講了 IoC 容器初始化的準(zhǔn)備階段蜕衡,即找到 BeanDefinition 的 Resource 定位突硝,就好比我們用水桶打水,首先要找到水源所在蛮瞄。找到水源之后所坯,我們關(guān)注的就是打水的過程了,相比于之前挂捅,這個(gè)過程更加的精妙芹助,下面我們一起來了解一下 IoC 容器初始化的第二個(gè)過程: BeanDefinition 的載入和解析

  • BeanDefinition 的載入和解析

    在完成對(duì) BeanDefinition 的 Resource 定位的分析之后,接下來我們來了解整個(gè) BeanDefinition 信息的載入過程。對(duì)于 IoC 容器而言状土,這個(gè)載入相當(dāng)于把定義的 BeanDefinition 在 IoC 容器中轉(zhuǎn)化成 Spring 內(nèi)部表示的數(shù)據(jù)結(jié)構(gòu)的過程无蜂。 IoC 容器對(duì) Bean 的管理和依賴注入功能的實(shí)現(xiàn),是通過其持有的 BeanDefinition 進(jìn)行各種相關(guān)操作來完成的蒙谓。這些 BeanDefinition 數(shù)據(jù)在 IoC 容器中通過一個(gè) HashMap 來保持和維護(hù)斥季。下面,我們從源碼出發(fā)來看一下 IoC 容器是如何對(duì) BeanDefinition 載入的累驮。

    BeanDefinition 載入的具體交互過程如下

    BeanDefinition 載入交互過程

    • 在上一篇文章中我們說過酣倾,refresh() 是一個(gè)非常重要的方法,是 IoC 容器初始化的入口谤专,那么我們找到其實(shí)現(xiàn)的源碼躁锡。它首先是在 FileSystemXmlApplicationContext 中調(diào)用,并在 AbstractApplicationContext 中被實(shí)現(xiàn)置侍。
      public void refresh() throws BeansException, IllegalStateException {
          synchronized (this.startupShutdownMonitor) {
              // Prepare this context for refreshing.
              prepareRefresh();
      
              // Tell the subclass to refresh the internal bean factory.
              ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
      
              // Prepare the bean factory for use in this context.
              prepareBeanFactory(beanFactory);
      
              try {
                  // Allows post-processing of the bean factory in context subclasses.
                  postProcessBeanFactory(beanFactory);
      
                  // Invoke factory processors registered as beans in the context.
                  invokeBeanFactoryPostProcessors(beanFactory);
      
                  // Register bean processors that intercept bean creation.
                  registerBeanPostProcessors(beanFactory);
      
                  // Initialize message source for this context.
                  initMessageSource();
      
                  // Initialize event multicaster for this context.
                  initApplicationEventMulticaster();
      
                  // Initialize other special beans in specific context subclasses.
                  onRefresh();
      
                  // Check for listener beans and register them.
                  registerListeners();
      
                  // Instantiate all remaining (non-lazy-init) singletons.
                  finishBeanFactoryInitialization(beanFactory);
      
                  // Last step: publish corresponding event.
                  finishRefresh();
              }
      
              catch (BeansException ex) {
                  // 為防止資源占用映之,在異常處理中,銷毀掉前面已經(jīng)生成的單例 Bean 
                  destroyBeans();
      
                  // Reset 'active' flag.
                  cancelRefresh(ex);
      
                  // Propagate exception to caller.
                  throw ex;
              }
          }
      }
      
      該方法詳細(xì)地描述了整個(gè) ApplicationContext 的初始化過程蜡坊,比如 BeanFactory 的更新等杠输,可以看成是對(duì) ApplicationContext 初始化的模板或執(zhí)行提綱,這個(gè)執(zhí)行為 Bean 的生命周期管理提供了條件算色。熟悉 IoC 容器使用的讀者抬伺,從這一系列調(diào)用的名字大概就能了解整個(gè) ApplicationContext 初始化的主要內(nèi)容。同時(shí)在 try-catch 之前灾梦,我們可以看到首先調(diào)用了 obtainBeanFactory 方法來獲取一個(gè) BeanFactory峡钓,我們進(jìn)去看一下發(fā)生了什么。
    • 最終到 AbstractRefreshableApplicationContext 類的 refreshBeanFactory() 方法:
      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);
          }
      }
      
      在該方法中若河,首先判斷是否已經(jīng)存在了基礎(chǔ)的 BeanFactory 容器能岩,有的話就銷毀。接著調(diào)用 createBeanFactory() 方法創(chuàng)建了一個(gè) DefaultListableBeanFactory萧福。這也驗(yàn)證了我們?cè)谏弦晃恼f到的拉鹃,ApplicationContext 是在基礎(chǔ) BeanFactory 上添加了高級(jí)容器特征的 IoC 容器,而且大多數(shù)情況下是使用 DefaultListableBeanFactory 這個(gè)具有基礎(chǔ)容器功能的 BeanFactory鲫忍。
    • 接著最主要的就是 loadBeanDefinitions() 方法膏燕,但是在這里這只是一個(gè)抽象方法,在上面的交互圖我們可以看到悟民,其具體實(shí)現(xiàn)是在 AbstractXmlApplicationContext 中實(shí)現(xiàn)的坝辫。
      protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
          // 創(chuàng)建一個(gè) XmlBeanDefinitionReader,并通過回調(diào)設(shè)置到 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);
      }
      
      其實(shí)到了這里近忙,如果在上面有親自動(dòng)手追蹤 BeanDefinition 的 Resource 定位的讀者竭业,應(yīng)該會(huì)對(duì)當(dāng)前 AbstractXmlApplicationContext 這個(gè)類比較熟悉,因?yàn)槲覀兩厦嫣岬降墨@取 configuration 也是 在這個(gè)類中調(diào)用的及舍。這更加可以說明 refresh() 是 IoC 容器初始化的如果未辆,畢竟在上一個(gè)步驟中我們并沒有進(jìn)入到 refresh() 這個(gè)方法里面去查看。
    • 接著就是 loadBeanDefinitions 調(diào)用的地方锯玛,首先得到 BeanDefinition 的 Resource 定位咐柜,其具體過程已經(jīng)在上文講過,我們就不再介紹了更振,代碼清單如下:
      protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
          Resource[] configResources = getConfigResources();
          if (configResources != null) {
              reader.loadBeanDefinitions(configResources);
          }
          String[] configLocations = getConfigLocations();
          if (configLocations != null) {
              reader.loadBeanDefinitions(configLocations);
          }
      }
      
    • 通過對(duì)以上實(shí)現(xiàn)原理的分析炕桨,我們可以看到,refresh() 方法啟動(dòng)對(duì) IoC 容器的初始化肯腕,具體的過程是在 XmlBeanDefinitionReader 中完成的献宫。因?yàn)?Spring 對(duì)應(yīng)不用形式的 BeanDefinition,這里使用的是 XML 方式定義实撒,所以需要使用 XmlBeanDefinitionReader姊途,如果使用了其他 BeanDefinition 方式,就需要使用其他中來的 BeanDefinitionReader 來完成載入工作知态。這里 XmlBeanDefinitionReader 的父類 AbstractBeanDefinitionReader 已經(jīng)為這個(gè)載入工作做好了準(zhǔn)備捷兰。代碼如下:
      public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
          Assert.notNull(resources, "Resource array must not be null");
          int counter = 0;
          for (Resource resource : resources) {
              counter += loadBeanDefinitions(resource);
          }
          return counter;
      }
      
      但是這里 loadBeanDefinitions 僅僅是一個(gè)接口方法,具體的實(shí)現(xiàn)交由各個(gè)子類去完成负敏。下面我們進(jìn)去到 XmlBeanDefinitionReader 去查看實(shí)現(xiàn)過程贡茅。
      • 我們看一下源碼:
        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<EncodedResource>(4);
                this.resourcesCurrentlyBeingLoaded.set(currentResources);
            }
            if (!currentResources.add(encodedResource)) {
                throw new BeanDefinitionStoreException(
                        "Detected cyclic loading of " + encodedResource + " - check your import definitions!");
            }
            // 這里得到XML 文件,并得到 IO 的 InputStream 準(zhǔn)備進(jìn)行讀取其做。
            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 {
                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);
            }
        }
        
      • 感興趣的讀者可以到 DefaultDocumentLoader 里面看看如何得到 Document 對(duì)象,這里就不詳細(xì)分析的妖泄。我們關(guān)系的是 Spring 的 BeanDefinition 是如何按照 Spring 的 Bean 語義要求進(jìn)行解析并轉(zhuǎn)化成容器內(nèi)部數(shù)據(jù)結(jié)構(gòu)的驹沿。這個(gè)過程是在 registerBeanDefinitions() 方法實(shí)現(xiàn)的,還對(duì)載入的 Bean 數(shù)量進(jìn)行了統(tǒng)計(jì)蹈胡。
        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;
        }
        
        可以看到渊季,這個(gè)解析過程是在 documentReader 里面進(jìn)行的,這里使用的是 DefaultBeanDefinitionDocumentReader罚渐。
      • 我們繼續(xù)追蹤 registerBeanDefinitions() 方法却汉,并結(jié)合最上面的交互過程,得到方法調(diào)用棧圖下圖所示:
        image

        我們首先進(jìn)入到 DefaultBeanDefinitionDocumentReader 里面荷并,可以看到 processBeanDefinition 方法中病涨,調(diào)用了 BeanDefinitionParserDelegate 來最終完成這個(gè)整個(gè)解析過程,得到的結(jié)果由 BeanDefinitionHolder 來持有璧坟,源碼清單如下:
        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));
            }
        }
        
        BeanDefinitionHolder 是 BeanDefinition 對(duì)象類的封裝類既穆,封裝了 BeanDefinition、Bean 的名字和別名雀鹃,用它來向 IoC 容器注冊(cè)幻工。而具體的解析過程交由 BeanDefinitionParserDelegate 完成,感興趣的讀者可以繼續(xù)仔細(xì)最終研究黎茎。下面我們舉個(gè)例子來分析一下囊颅。
    • 我們先來看一下最常見的 Bean 元素解析:
      public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
          // 這里取得 bean 元素定義里面 id、name傅瞻、aliase 屬性的值踢代。
          String id = ele.getAttribute(ID_ATTRIBUTE);
          String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
      
          List<String> aliases = new ArrayList<String>();
          if (StringUtils.hasLength(nameAttr)) {
              String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
              aliases.addAll(Arrays.asList(nameArr));
          }
      
          String beanName = id;
          if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
              beanName = aliases.remove(0);
              if (logger.isDebugEnabled()) {
                  logger.debug("No XML 'id' specified - using '" + beanName +
                          "' as bean name and " + aliases + " as aliases");
              }
          }
      
          if (containingBean == null) {
              checkNameUniqueness(beanName, aliases, ele);
          }
          // 這個(gè)方法引發(fā)對(duì) bean 元素的詳細(xì)解析
          AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
          if (beanDefinition != null) {
              if (!StringUtils.hasText(beanName)) {
                  try {
                      if (containingBean != null) {
                          beanName = BeanDefinitionReaderUtils.generateBeanName(
                                  beanDefinition, this.readerContext.getRegistry(), true);
                      }
                      else {
                          beanName = this.readerContext.generateBeanName(beanDefinition);
                          String beanClassName = beanDefinition.getBeanClassName();
                          if (beanClassName != null &&
                                  beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
                                  !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
                              aliases.add(beanClassName);
                          }
                      }
                      if (logger.isDebugEnabled()) {
                          logger.debug("Neither XML 'id' nor 'name' specified - " +
                                  "using generated bean name [" + beanName + "]");
                      }
                  }
                  catch (Exception ex) {
                      error(ex.getMessage(), ele);
                      return null;
                  }
              }
              String[] aliasesArray = StringUtils.toStringArray(aliases);
              return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
          }
      
          return null;
      }
      
      在這里我們會(huì)看到 XML 定義文件常見到的屬性元素,如 id嗅骄、name胳挎、aliase 等,把這些元素從 XML 文件轉(zhuǎn)化而來的 element 中取出來溺森,并設(shè)置到 BeanDefinitionHolder 中去慕爬,這些屬性的解析還是比較簡(jiǎn)單的。對(duì)于其他元素配置的解析屏积,如各種 Bean 的屬性配置医窿,則為一個(gè)較為復(fù)雜的過程,由 parseBeanDefinitionElement 方法完成炊林。
      • 以上介紹了對(duì) Bean 元素進(jìn)行解析的過程姥卢。也就是 BeanDefinition 根據(jù) XML 的 <bean> 定義被創(chuàng)建的過程。這個(gè) BeanDefinition 可以看成 <bean> 定義的抽象渣聚。這個(gè)數(shù)據(jù)對(duì)象中封裝的數(shù)據(jù)大都是與 <bean> 定義相關(guān)的独榴,也就是我們?cè)诙x Bean 時(shí)看到的那些 Spring 標(biāo)記,如 init-method饵逐、destroy-method 等括眠。這個(gè) BeanDefinition 數(shù)據(jù)類型是非常重要的,它封裝了很多基本數(shù)據(jù)倍权,這些基本數(shù)據(jù)都是 IoC 容器需要的掷豺。 BeanDefinition 是 IoC 容器中非常核心的數(shù)據(jù)結(jié)構(gòu),而通過上述的解析薄声,這些數(shù)據(jù)已經(jīng)準(zhǔn)備好在 IoC 容器中大顯身手了当船。
      • 下面我們?cè)俳又櫍M(jìn)入 parseBeanDefinitionElement 源碼之中:
        public AbstractBeanDefinition parseBeanDefinitionElement(
                Element ele, String beanName, BeanDefinition containingBean) {
        
            this.parseState.push(new BeanEntry(beanName));
        
            String className = null;
            if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
                className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
            }
        
            try {
                String parent = null;
                if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
                    parent = ele.getAttribute(PARENT_ATTRIBUTE);
                }
                AbstractBeanDefinition bd = createBeanDefinition(className, parent);
                // 這里對(duì)當(dāng)前的 Bean 元素進(jìn)行屬性分析默辨,并設(shè)置描述信息德频。
                parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
                bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
                // 從名字可以看出,這里是對(duì)各種 <bean> 元素的信息進(jìn)行解析的地方缩幸。
                parseMetaElements(ele, bd);
                parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
                parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
        
                parseConstructorArgElements(ele, bd);
                parsePropertyElements(ele, bd);
                parseQualifierElements(ele, bd);
        
                bd.setResource(this.readerContext.getResource());
                bd.setSource(extractSource(ele));
        
                return bd;
            }
            catch (ClassNotFoundException ex) {
                error("Bean class [" + className + "] not found", ele, ex);
            }
            catch (NoClassDefFoundError err) {
                error("Class that bean class [" + className + "] depends on not found", ele, err);
            }
            catch (Throwable ex) {
                error("Unexpected failure during bean definition parsing", ele, ex);
            }
            finally {
                this.parseState.pop();
            }
        
            return null;
        }
        
      • 上面是具體生成 BeanDefinition 的地方壹置。在這里竞思,我們舉一個(gè)對(duì) property 進(jìn)行解析的例子,最終完成對(duì)整個(gè) BeanDefinition 載入和解析的過程钞护。這里是指對(duì) Bean 元素下的 property 子元素進(jìn)行解析盖喷。
        public void parsePropertyElements(Element beanEle, BeanDefinition bd) {
            // 遍歷 Bean 元素下的定義的 property 
            NodeList nl = beanEle.getChildNodes();
            
            for (int i = 0; i < nl.getLength(); i++) {
                Node node = nl.item(i);
                if (isCandidateElement(node) && nodeNameEquals(node, PROPERTY_ELEMENT)) {
                    // 進(jìn)行詳細(xì)的解析
                    parsePropertyElement((Element) node, bd);
                }
            }
        }
        
        public void parsePropertyElement(Element ele, BeanDefinition bd) {
            / 這里取得 property 的名字。
            String propertyName = ele.getAttribute(NAME_ATTRIBUTE);
            if (!StringUtils.hasLength(propertyName)) {
                error("Tag 'property' must have a 'name' attribute", ele);
                return;
            }
            this.parseState.push(new PropertyEntry(propertyName));
            // 這里是解析 property 的過程难咕。返回的對(duì)象對(duì)應(yīng)在 Bean 中定義的 property 屬性的解析結(jié)果课梳,這個(gè)結(jié)果會(huì)封裝到 PropertyValue 中。
            try {
                if (bd.getPropertyValues().contains(propertyName)) {
                    error("Multiple 'property' definitions for property '" + propertyName + "'", ele);
                    return;
                }
                Object val = parsePropertyValue(ele, bd, propertyName);
                PropertyValue pv = new PropertyValue(propertyName, val);
                parseMetaElements(ele, pv);
                pv.setSource(extractSource(ele));
                bd.getPropertyValues().addPropertyValue(pv);
            }
            finally {
                this.parseState.pop();
            }
        }
        
        // 這里取得 peoperty 元素的值
        public Object parsePropertyValue(Element ele, BeanDefinition bd, String propertyName) {
            String elementName = (propertyName != null) ?
                            "<property> element for property '" + propertyName + "'" :
                            "<constructor-arg> element";
        
            // Should only have one child element: ref, value, list, etc.
            NodeList nl = ele.getChildNodes();
            Element subElement = null;
            for (int i = 0; i < nl.getLength(); i++) {
                Node node = nl.item(i);
                if (node instanceof Element && !nodeNameEquals(node, DESCRIPTION_ELEMENT) &&
                        !nodeNameEquals(node, META_ELEMENT)) {
                    // Child element is what we're looking for.
                    if (subElement != null) {
                        error(elementName + " must not contain more than one sub-element", ele);
                    }
                    else {
                        subElement = (Element) node;
                    }
                }
            }
        
            boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE);
            boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE);
            if ((hasRefAttribute && hasValueAttribute) ||
                    ((hasRefAttribute || hasValueAttribute) && subElement != null)) {
                error(elementName +
                        " is only allowed to contain either 'ref' attribute OR 'value' attribute OR sub-element", ele);
            }
        
            if (hasRefAttribute) {
                String refName = ele.getAttribute(REF_ATTRIBUTE);
                if (!StringUtils.hasText(refName)) {
                    error(elementName + " contains empty 'ref' attribute", ele);
                }
                RuntimeBeanReference ref = new RuntimeBeanReference(refName);
                ref.setSource(extractSource(ele));
                return ref;
            }
            else if (hasValueAttribute) {
                TypedStringValue valueHolder = new TypedStringValue(ele.getAttribute(VALUE_ATTRIBUTE));
                valueHolder.setSource(extractSource(ele));
                return valueHolder;
            }
            else if (subElement != null) {
                return parsePropertySubElement(subElement, bd);
            }
            else {
                // Neither child element nor "ref" or "value" attribute found.
                error(elementName + " must specify a ref or value", ele);
                return null;
            }
        }
        
        // 這里是對(duì) property 子元素的解析過程余佃,Array暮刃、List、Set爆土、Map 等元素都會(huì)在這里解析
        public Object parsePropertySubElement(Element ele, BeanDefinition bd, String defaultValueType) {
            if (!isDefaultNamespace(ele)) {
                return parseNestedCustomElement(ele, bd);
            }
            else if (nodeNameEquals(ele, BEAN_ELEMENT)) {
                BeanDefinitionHolder nestedBd = parseBeanDefinitionElement(ele, bd);
                if (nestedBd != null) {
                    nestedBd = decorateBeanDefinitionIfRequired(ele, nestedBd, bd);
                }
                return nestedBd;
            }
            else if (nodeNameEquals(ele, REF_ELEMENT)) {
                // A generic reference to any name of any bean.
                String refName = ele.getAttribute(BEAN_REF_ATTRIBUTE);
                boolean toParent = false;
                if (!StringUtils.hasLength(refName)) {
                    // A reference to the id of another bean in the same XML file.
                    refName = ele.getAttribute(LOCAL_REF_ATTRIBUTE);
                    if (!StringUtils.hasLength(refName)) {
                        // A reference to the id of another bean in a parent context.
                        refName = ele.getAttribute(PARENT_REF_ATTRIBUTE);
                        toParent = true;
                        if (!StringUtils.hasLength(refName)) {
                            error("'bean', 'local' or 'parent' is required for <ref> element", ele);
                            return null;
                        }
                    }
                }
                if (!StringUtils.hasText(refName)) {
                    error("<ref> element contains empty target attribute", ele);
                    return null;
                }
                RuntimeBeanReference ref = new RuntimeBeanReference(refName, toParent);
                ref.setSource(extractSource(ele));
                return ref;
            }
            else if (nodeNameEquals(ele, IDREF_ELEMENT)) {
                return parseIdRefElement(ele);
            }
            else if (nodeNameEquals(ele, VALUE_ELEMENT)) {
                return parseValueElement(ele, defaultValueType);
            }
            else if (nodeNameEquals(ele, NULL_ELEMENT)) {
                // It's a distinguished null value. Let's wrap it in a TypedStringValue
                // object in order to preserve the source location.
                TypedStringValue nullHolder = new TypedStringValue(null);
                nullHolder.setSource(extractSource(ele));
                return nullHolder;
            }
            else if (nodeNameEquals(ele, ARRAY_ELEMENT)) {
                return parseArrayElement(ele, bd);
            }
            else if (nodeNameEquals(ele, LIST_ELEMENT)) {
                return parseListElement(ele, bd);
            }
            else if (nodeNameEquals(ele, SET_ELEMENT)) {
                return parseSetElement(ele, bd);
            }
            else if (nodeNameEquals(ele, MAP_ELEMENT)) {
                return parseMapElement(ele, bd);
            }
            else if (nodeNameEquals(ele, PROPS_ELEMENT)) {
                return parsePropsElement(ele);
            }
            else {
                error("Unknown property sub-element: [" + ele.getNodeName() + "]", ele);
                return null;
            }
        }
        
        property 子元素的解析椭懊,最終會(huì)生成對(duì)應(yīng)的數(shù)據(jù)對(duì)象,比如 ManagedList雾消、ManagedArray灾搏、ManagedSet等,這些 Managed 類是 Spring 的具體的 BeanDefinition 的數(shù)據(jù)封裝立润。具體的過程讀者可以去查看具體的解析過程狂窑。從一系列 parse 方法名字可以很清楚的看出是對(duì)哪種類型的解析,具體的過程我們就不再查看了桑腮。

這樣逐層的解析泉哈,我們?cè)?XML 定義的 BeanDefinition 就被整個(gè)載入到 IoC 容器中,并在容器中建立了數(shù)據(jù)映射破讨,即在 IoC 容器創(chuàng)建了對(duì)應(yīng)的數(shù)據(jù)結(jié)構(gòu)丛晦,這些數(shù)據(jù)結(jié)構(gòu)以 AbstractBeanDefinition 為入口,讓 IoC 容器進(jìn)行索引提陶、查詢和操作烫沙。但是,重要的依賴注入實(shí)際上還沒有發(fā)生隙笆,現(xiàn)在 IoC 容器 BeanDefinition 中存在的還只是一些靜態(tài)的配置锌蓄。嚴(yán)格來說,這時(shí)候的容器還沒有完全起作用撑柔,要完全發(fā)揮容器的作用瘸爽,還需要完成數(shù)據(jù)向容器的注冊(cè)。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末铅忿,一起剝皮案震驚了整個(gè)濱河市剪决,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖柑潦,帶你破解...
    沈念sama閱讀 218,755評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件享言,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡渗鬼,警方通過查閱死者的電腦和手機(jī)担锤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來乍钻,“玉大人,你說我怎么就攤上這事铭腕∫瘢” “怎么了?”我有些...
    開封第一講書人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵累舷,是天一觀的道長(zhǎng)浩考。 經(jīng)常有香客問我,道長(zhǎng)被盈,這世上最難降的妖魔是什么析孽? 我笑而不...
    開封第一講書人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮只怎,結(jié)果婚禮上袜瞬,老公的妹妹穿的比我還像新娘。我一直安慰自己身堡,他們只是感情好邓尤,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著贴谎,像睡著了一般汞扎。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上擅这,一...
    開封第一講書人閱讀 51,631評(píng)論 1 305
  • 那天澈魄,我揣著相機(jī)與錄音,去河邊找鬼仲翎。 笑死痹扇,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的谭确。 我是一名探鬼主播帘营,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼逐哈!你這毒婦竟也來了芬迄?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤昂秃,失蹤者是張志新(化名)和其女友劉穎禀梳,沒想到半個(gè)月后杜窄,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,724評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡算途,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年屁使,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片斟览。...
    茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡轴猎,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出廓脆,到底是詐尸還是另有隱情筛谚,我是刑警寧澤,帶...
    沈念sama閱讀 35,742評(píng)論 5 346
  • 正文 年R本政府宣布停忿,位于F島的核電站驾讲,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏席赂。R本人自食惡果不足惜吮铭,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望颅停。 院中可真熱鬧谓晌,春花似錦、人聲如沸便监。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽烧董。三九已至毁靶,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間逊移,已是汗流浹背预吆。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留胳泉,地道東北人拐叉。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像扇商,于是被迫代替她去往敵國和親凤瘦。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容