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阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容