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

在上一篇文章,我們講了 IoC 容器初始化的準備階段,即找到 BeanDefinition 的 Resource 定位,就好比我們用水桶打水,首先要找到水源所在。找到水源之后,我們關注的就是打水的過程了,相比于之前,這個過程更加的精妙,下面我們一起來了解一下 IoC 容器初始化的第二個過程: BeanDefinition 的載入和解析

  • BeanDefinition 的載入和解析

    在完成對 BeanDefinition 的 Resource 定位的分析之后,接下來我們來了解整個 BeanDefinition 信息的載入過程。對于 IoC 容器而言,這個載入相當于把定義的 BeanDefinition 在 IoC 容器中轉化成 Spring 內部表示的數據結構的過程。 IoC 容器對 Bean 的管理和依賴注入功能的實現,是通過其持有的 BeanDefinition 進行各種相關操作來完成的。這些 BeanDefinition 數據在 IoC 容器中通過一個 HashMap 來保持和維護。下面,我們從源碼出發來看一下 IoC 容器是如何對 BeanDefinition 載入的。

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

    BeanDefinition 載入交互過程

    • 在上一篇文章中我們說過,refresh() 是一個非常重要的方法,是 IoC 容器初始化的入口,那么我們找到其實現的源碼。它首先是在 FileSystemXmlApplicationContext 中調用,并在 AbstractApplicationContext 中被實現。
      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) {
                  // 為防止資源占用,在異常處理中,銷毀掉前面已經生成的單例 Bean 
                  destroyBeans();
      
                  // Reset 'active' flag.
                  cancelRefresh(ex);
      
                  // Propagate exception to caller.
                  throw ex;
              }
          }
      }
      
      該方法詳細地描述了整個 ApplicationContext 的初始化過程,比如 BeanFactory 的更新等,可以看成是對 ApplicationContext 初始化的模板或執行提綱,這個執行為 Bean 的生命周期管理提供了條件。熟悉 IoC 容器使用的讀者,從這一系列調用的名字大概就能了解整個 ApplicationContext 初始化的主要內容。同時在 try-catch 之前,我們可以看到首先調用了 obtainBeanFactory 方法來獲取一個 BeanFactory,我們進去看一下發生了什么。
    • 最終到 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);
          }
      }
      
      在該方法中,首先判斷是否已經存在了基礎的 BeanFactory 容器,有的話就銷毀。接著調用 createBeanFactory() 方法創建了一個 DefaultListableBeanFactory。這也驗證了我們在上一文說到的,ApplicationContext 是在基礎 BeanFactory 上添加了高級容器特征的 IoC 容器,而且大多數情況下是使用 DefaultListableBeanFactory 這個具有基礎容器功能的 BeanFactory。
    • 接著最主要的就是 loadBeanDefinitions() 方法,但是在這里這只是一個抽象方法,在上面的交互圖我們可以看到,其具體實現是在 AbstractXmlApplicationContext 中實現的。
      protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
          // 創建一個 XmlBeanDefinitionReader,并通過回調設置到 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);
      }
      
      其實到了這里,如果在上面有親自動手追蹤 BeanDefinition 的 Resource 定位的讀者,應該會對當前 AbstractXmlApplicationContext 這個類比較熟悉,因為我們上面提到的獲取 configuration 也是 在這個類中調用的。這更加可以說明 refresh() 是 IoC 容器初始化的如果,畢竟在上一個步驟中我們并沒有進入到 refresh() 這個方法里面去查看。
    • 接著就是 loadBeanDefinitions 調用的地方,首先得到 BeanDefinition 的 Resource 定位,其具體過程已經在上文講過,我們就不再介紹了,代碼清單如下:
      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);
          }
      }
      
    • 通過對以上實現原理的分析,我們可以看到,refresh() 方法啟動對 IoC 容器的初始化,具體的過程是在 XmlBeanDefinitionReader 中完成的。因為 Spring 對應不用形式的 BeanDefinition,這里使用的是 XML 方式定義,所以需要使用 XmlBeanDefinitionReader,如果使用了其他 BeanDefinition 方式,就需要使用其他中來的 BeanDefinitionReader 來完成載入工作。這里 XmlBeanDefinitionReader 的父類 AbstractBeanDefinitionReader 已經為這個載入工作做好了準備。代碼如下:
      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 僅僅是一個接口方法,具體的實現交由各個子類去完成。下面我們進去到 XmlBeanDefinitionReader 去查看實現過程。
      • 我們看一下源碼:
        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 準備進行讀取。
            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 對象,這里就不詳細分析的。我們關系的是 Spring 的 BeanDefinition 是如何按照 Spring 的 Bean 語義要求進行解析并轉化成容器內部數據結構的。這個過程是在 registerBeanDefinitions() 方法實現的,還對載入的 Bean 數量進行了統計。
        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;
        }
        
        可以看到,這個解析過程是在 documentReader 里面進行的,這里使用的是 DefaultBeanDefinitionDocumentReader。
      • 我們繼續追蹤 registerBeanDefinitions() 方法,并結合最上面的交互過程,得到方法調用棧圖下圖所示:
        image

        我們首先進入到 DefaultBeanDefinitionDocumentReader 里面,可以看到 processBeanDefinition 方法中,調用了 BeanDefinitionParserDelegate 來最終完成這個整個解析過程,得到的結果由 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 對象類的封裝類,封裝了 BeanDefinition、Bean 的名字和別名,用它來向 IoC 容器注冊。而具體的解析過程交由 BeanDefinitionParserDelegate 完成,感興趣的讀者可以繼續仔細最終研究。下面我們舉個例子來分析一下。
    • 我們先來看一下最常見的 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);
          }
          // 這個方法引發對 bean 元素的詳細解析
          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;
      }
      
      在這里我們會看到 XML 定義文件常見到的屬性元素,如 id、name、aliase 等,把這些元素從 XML 文件轉化而來的 element 中取出來,并設置到 BeanDefinitionHolder 中去,這些屬性的解析還是比較簡單的。對于其他元素配置的解析,如各種 Bean 的屬性配置,則為一個較為復雜的過程,由 parseBeanDefinitionElement 方法完成。
      • 以上介紹了對 Bean 元素進行解析的過程。也就是 BeanDefinition 根據 XML 的 <bean> 定義被創建的過程。這個 BeanDefinition 可以看成 <bean> 定義的抽象。這個數據對象中封裝的數據大都是與 <bean> 定義相關的,也就是我們在定義 Bean 時看到的那些 Spring 標記,如 init-method、destroy-method 等。這個 BeanDefinition 數據類型是非常重要的,它封裝了很多基本數據,這些基本數據都是 IoC 容器需要的。 BeanDefinition 是 IoC 容器中非常核心的數據結構,而通過上述的解析,這些數據已經準備好在 IoC 容器中大顯身手了。
      • 下面我們再接著跟蹤,進入 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);
                // 這里對當前的 Bean 元素進行屬性分析,并設置描述信息。
                parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
                bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
                // 從名字可以看出,這里是對各種 <bean> 元素的信息進行解析的地方。
                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 的地方。在這里,我們舉一個對 property 進行解析的例子,最終完成對整個 BeanDefinition 載入和解析的過程。這里是指對 Bean 元素下的 property 子元素進行解析。
        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)) {
                    // 進行詳細的解析
                    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 的過程。返回的對象對應在 Bean 中定義的 property 屬性的解析結果,這個結果會封裝到 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;
            }
        }
        
        // 這里是對 property 子元素的解析過程,Array、List、Set、Map 等元素都會在這里解析
        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 子元素的解析,最終會生成對應的數據對象,比如 ManagedList、ManagedArray、ManagedSet等,這些 Managed 類是 Spring 的具體的 BeanDefinition 的數據封裝。具體的過程讀者可以去查看具體的解析過程。從一系列 parse 方法名字可以很清楚的看出是對哪種類型的解析,具體的過程我們就不再查看了。

這樣逐層的解析,我們在 XML 定義的 BeanDefinition 就被整個載入到 IoC 容器中,并在容器中建立了數據映射,即在 IoC 容器創建了對應的數據結構,這些數據結構以 AbstractBeanDefinition 為入口,讓 IoC 容器進行索引、查詢和操作。但是,重要的依賴注入實際上還沒有發生,現在 IoC 容器 BeanDefinition 中存在的還只是一些靜態的配置。嚴格來說,這時候的容器還沒有完全起作用,要完全發揮容器的作用,還需要完成數據向容器的注冊。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,182評論 6 543
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,489評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,290評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,776評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,510評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,866評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,860評論 3 447
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,036評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,585評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,331評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,536評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,058評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,754評論 3 349
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,154評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,469評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,273評論 3 399
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,505評論 2 379

推薦閱讀更多精彩內容