本来这一篇是要写如何加载所有的单实例bean对象,但是回顾第二篇,发现对于解析xml文件加载bean定义信息的部分理解表达的并不是很好,所以在此补充一篇Spring解析配置文件,加载bean定义信息的文章。

1.以refresh()作为抓手

**refresh()**作为容器的刷新方法,重要性不必多说,在里面的第二个方法**obtainFreshBeanFactory()**里面,解析了xml配置文件或注解,载入bean定义资源信息,返回了一个全新的bean工厂。接下来来分析**obtainFreshBeanFactory()**

2.obtainFreshBeanFactory()

解析xml文件或者注解,加载bean 的定义信息,创建一个全新的bean工厂。

  1. protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
  2. //刷新bean工厂
  3. refreshBeanFactory();
  4. /**返回bean工厂*/
  5. return getBeanFactory();
  6. }

3.refreshBeanFactory()

这里面主要就是判断当前有没有bean工厂,如果有的话,就把工厂关了,重新造一个,如果没有的话,直接造一个。总之一定要造一个新的工厂。**createBeanFactory()**

  1. @Override
  2. protected final void refreshBeanFactory() throws BeansException {
  3. /*如果已经有了bean工厂,通常情况下并不会,什么情况下会有?通过applicationContext直接调用refresh方法*/
  4. if (hasBeanFactory()) {
  5. /*销毁里面的所有bean*/
  6. destroyBeans();
  7. /*关闭bean工厂*/
  8. closeBeanFactory();
  9. }
  10. try {
  11. //不管上面是否已经有bean工厂存在,最终都会走到这里,去创建一个bean工厂
  12. DefaultListableBeanFactory beanFactory = createBeanFactory();
  13. /*设置序列化id*/
  14. beanFactory.setSerializationId(getId());
  15. /*对工厂进行一些定制化设置*/
  16. customizeBeanFactory(beanFactory);
  17. /*加载bean的定义信息*/
  18. loadBeanDefinitions(beanFactory);
  19. /*将当前类的bean工厂引用指向创建的bean工厂*/
  20. this.beanFactory = beanFactory;
  21. }
  22. catch (IOException ex) {
  23. throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
  24. }
  25. }

**createBeanFactory()**
返回一个全新的bean工厂。

  1. protected DefaultListableBeanFactory createBeanFactory() {
  2. return new DefaultListableBeanFactory(getInternalParentBeanFactory());
  3. }

每次容器刷新的时候尝试调用此方法。默认创建一个**DefaultListableBeanFactory**,并将此上下文父级的内部bean工厂作为父bean工厂。

可以再子类中覆盖,例如自定义**DefaultListableBeanFactory**的设置。

创建完工厂了,就要往工厂里面放东西,** loadBeanDefinitions(beanFactory)**

4.loadBeanDefinitions(beanFactory)

载入bean的定义信息 **beanDefinition**

  1. @Override
  2. protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
  3. // 创建一个xml 的beanDefinition加载器 这个玩意里面持有一个beanFactory的引用
  4. XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
  5. /*给beanDefinition 加载器设置上下文环境 资源加载器 实体解析器*/
  6. /*这玩意我记得都是用的默认的*/
  7. beanDefinitionReader.setEnvironment(this.getEnvironment());
  8. beanDefinitionReader.setResourceLoader(this);
  9. beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
  10. /*初始化beanDefinition加载器*/
  11. initBeanDefinitionReader(beanDefinitionReader);
  12. /*使用beanDefinition加载器加载beanDefinitions*/
  13. loadBeanDefinitions(beanDefinitionReader);
  14. }

首先创建了一个bean定义信息的加载器 **XmlBeanDefinitionReader**,用来解析xml文件。

然后给**XmlBeanDefinitionReader**设置一些相关信息。

然后初始化 **XmlBeanDefinitionReader****initBeanDefinitionReader(beanDefinitionReader)**

最后使用**XmlBeanDefinitionReader**加载bean的定义信息。**loadBeanDefinitions(beanDefinitionReader)**

5.bean定义信息加载器

**XmlBeanDefinitionReader**

  1. public XmlBeanDefinitionReader(BeanDefinitionRegistry registry) {
  2. super(registry);
  3. }

构造器里面传入了一个**BeanDefinitionRegistry**,给父类**AbstractBeanDefinitionReader**的属性赋值。

6.Bean定义信息注册器

**BeanDefinitionRegistry**

持有bean定义的注册表接口。例如_**RootBeanDefinition**__**ChildBeanDefinition**_ 实例。

通常由在内部使用_**AbstractBeanDefinition**_层次结构的**beanFactory**实现。

这是spring的bean工厂中唯一封装**BeanDefinitionRegistry**的接口。标准的**BeanFactory**接口仅仅涵盖对完全配置的工厂的工厂实例的访问。

spring的**BeanDefinitionRegistry**期待作用于该接口的实现。spring中已经有的实现是_**DefaultListableBeanFactory**_ & _**GenericApplicationContext**_

  1. public interface BeanDefinitionRegistry extends AliasRegistry {
  2. /*注册一个bean的定义信息,支持指定bean的名字*/
  3. void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
  4. throws BeanDefinitionStoreException;
  5. /*根据bean的名字移除掉已经注册的beanDefinition*/
  6. void removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;
  7. /*根据名字查找BeanDefinition*/
  8. BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;
  9. /*判断是否包含给定名字的BeanDefinition信息*/
  10. boolean containsBeanDefinition(String beanName);
  11. /*返回已经注册的BeanDefinition名字列表*/
  12. String[] getBeanDefinitionNames();
  13. /*返回已经注册的BeanDefinition数量*/
  14. int getBeanDefinitionCount();
  15. /*确定给定的bean名称是否已经在注册中心使用, 怎么判断是否使用呢?就是是否有别名注册在此名称下,或者已经有注册在这里的bean。*/
  16. boolean isBeanNameInUse(String beanName);
  17. }

7.初始化beanDefinition的加载器

**initBeanDefinitionReader(beanDefinitionReader)**

  1. protected void initBeanDefinitionReader(XmlBeanDefinitionReader reader) {
  2. reader.setValidating(this.validating);
  3. }

初始化用于加载次上下文的beanDefinition的beanDefinitionReader。默认实现为空。可以再子类中覆盖,例如关闭xml验证或使用不同的_**XmlBeanDefinitionParser**_实现。

8.加载bean的定义信息

**loadBeanDefinitions(beanDefinitionReader)**

使用给定的_**XmlBeanDefinitionReader**_加载**beanDefinition**

**BeanFactory**的生命周期由_**refreshBeanFactory() **_处理,因此此方法仅用于加载和注册**beanDefinition**

  1. protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
  2. /*这里实际上是一个钩子方法,经典的模板模式,子类根据需要对方法进行重写,实际上加载xml的时候,这里锤子也没拿到*/
  3. Resource[] configResources = getConfigResources();
  4. /*如果资源不为空,走这里的逻辑,但是上面已经分析过,实际上锤子也没拿到,所以走下面的逻辑*/
  5. if (configResources != null) {
  6. reader.loadBeanDefinitions(configResources);
  7. }
  8. /*获取配置文件位置*/
  9. String[] configLocations = getConfigLocations();
  10. /*此时读取到了我们在配置文件指定的配置文件 beans.xml*/
  11. if (configLocations != null) {
  12. reader.loadBeanDefinitions(configLocations);
  13. }
  14. }

在这里我们注意两个方法:

  1. **getConfigLocations()** 获取到配置文件的位置
  2. **reader.loadBeanDefinitions(configLocations)** 使用bean定义信息的加载器加载**beanDefinition**


  1. @Nullable
  2. protected String[] getConfigLocations() {
  3. return (this.configLocations != null ? this.configLocations : getDefaultConfigLocations());
  4. }

返回一个资源位置数组,指的是构建此上下文应用的时候使用的 xml配置文件。还可以包括位置模式,这将通过_**ResourcePatternResolver**_处理。默认实现返回null。子类可以重写这个方法用来提供一组资源位置来加载**beanDefinition**

9.reader.loadBeanDefinitions(configLocations)

用bean定义信息的加载器加载**beanDefinition**

  1. @Override
  2. public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {
  3. /*断言 判空*/
  4. Assert.notNull(locations, "Location array must not be null");
  5. /*记录beanDefinition的数量*/
  6. int count = 0;
  7. /*迭代加载beanDefinition*/
  8. for (String location : locations) {
  9. count += loadBeanDefinitions(location);
  10. }
  11. return count;
  12. }

这里是根据配置文件的位置数组进行循环迭代加载并记录**beanDefinition**的数量。

来到了**AbstractBeanDefinitionReader****loadBeanDefinitions()**

  1. public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
  2. /*获取资源加载器*/
  3. ResourceLoader resourceLoader = getResourceLoader();
  4. /*如果资源加载器为空,抛异常*/
  5. if (resourceLoader == null) {
  6. throw new BeanDefinitionStoreException(
  7. "Cannot load bean definitions from location [" + location + "]: no ResourceLoader available");
  8. }
  9. /*如果资源加载器是资源模式解析器类型的*/
  10. if (resourceLoader instanceof ResourcePatternResolver) {
  11. // Resource pattern matching available.
  12. try {
  13. /*
  14. 将xml配置文件加载到resources中,resource其实就是spring底层封装了很多的细节,
  15. 抽象出来的资源顶层接口
  16. 让开发人员不必专注于底层配置文件的加载细节
  17. */
  18. Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
  19. /*加载beanDefinition*/
  20. int count = loadBeanDefinitions(resources);
  21. /*这玩意不知道是啥,反正是空,没啥锤子用*/
  22. if (actualResources != null) {
  23. Collections.addAll(actualResources, resources);
  24. }
  25. if (logger.isTraceEnabled()) {
  26. logger.trace("Loaded " + count + " bean definitions from location pattern [" + location + "]");
  27. }
  28. return count;
  29. }
  30. catch (IOException ex) {
  31. throw new BeanDefinitionStoreException(
  32. "Could not resolve bean definition resource pattern [" + location + "]", ex);
  33. }
  34. }/*走到这里说明资源加载器肯定不是资源模式解析器类型的*/
  35. else {
  36. // 只能通过绝对网址加载单个资源
  37. Resource resource = resourceLoader.getResource(location);
  38. int count = loadBeanDefinitions(resource);
  39. if (actualResources != null) {
  40. actualResources.add(resource);
  41. }
  42. if (logger.isTraceEnabled()) {
  43. logger.trace("Loaded " + count + " bean definitions from location [" + location + "]");
  44. }
  45. return count;
  46. }
  47. }

首先获取到资源的加载器,如果加载器是空,那就说明程序无法往下执行了,直接抛异常。

如果资源加载器是资源解析器模式的,加载xml文件到**Resource**数组中,**Resource**是什么在上一篇中已经介绍过了,在此不再赘述。

此时在根据**Resource**数组去加载**beanDefinition**,最后返回加载的**beanDefinition**的数量。(注意:这里走的是else的逻辑,因为默认我们不是位置模式。)

注意这个时候思路已经很明确了,准备了这么多实际上到这里就分为了两步:

  1. 加载xml配置文件 **getResource(location)**
  2. 通过xml配置文件去加载**beanDefinition** **loadBeanDefinitions(resource)**


10.加载xml配置文件

来到**DefaultResourceLoader**

  1. @Override
  2. public Resource getResource(String location) {
  3. Assert.notNull(location, "Location must not be null");
  4. //循环遍历使用解析器解析该location的资源,如果资源不为空,直接返回
  5. for (ProtocolResolver protocolResolver : getProtocolResolvers()) {
  6. Resource resource = protocolResolver.resolve(location, this);
  7. if (resource != null) {
  8. return resource;
  9. }
  10. }
  11. /*以/开头那么根据path去寻找*/
  12. if (location.startsWith("/")) {
  13. return getResourceByPath(location);
  14. }
  15. /*以classpath开头,那么抽象为ClassPathResource*/
  16. else if (location.startsWith(CLASSPATH_URL_PREFIX)) {
  17. return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());
  18. }
  19. else {
  20. try {
  21. /*其他情况采用urlResource来加载*/
  22. URL url = new URL(location);
  23. return (ResourceUtils.isFileURL(url) ? new FileUrlResource(url) : new UrlResource(url));
  24. }
  25. catch (MalformedURLException ex) {
  26. // No URL -> resolve as resource path.
  27. return getResourceByPath(location);
  28. }
  29. }
  30. }

接下来就是加载**beanDefinition**信息。

11.加载beanDefinition

**loadBeanDefinitions(resource)**

来到了 **XmlBeanDefinitionReader**

  1. @Override
  2. public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
  3. //1.将resource包装成带有编码格式的EncodedResource
  4. //2.重载调用loadBeanDefinitions()
  5. return loadBeanDefinitions(new EncodedResource(resource));
  6. }

我们来到重载的方法。**loadBeanDefinitions(resource)**

  1. public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
  2. //跳过断言 日志
  3. Assert.notNull(encodedResource, "EncodedResource must not be null");
  4. if (logger.isTraceEnabled()) {
  5. logger.trace("Loading XML bean definitions from " + encodedResource);
  6. }
  7. //获取引用:当前线程已经加载过的encodingResource资源
  8. Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
  9. //将当前的encodingResource加入到threadlocal的set中,加入失败说明当前资源已经加载过了,不能重复加载,需要抛出异常
  10. if (!currentResources.add(encodedResource)) {
  11. throw new BeanDefinitionStoreException(
  12. "Detected cyclic loading of " + encodedResource + " - check your import definitions!");
  13. }
  14. //jdk新版本的语法糖 拿到资源的输入流
  15. try (InputStream inputStream = encodedResource.getResource().getInputStream()) {
  16. //因为接下来要使用 sax解析器,解析xml文件 ,所以需要将输入流包装成inputsource,
  17. //inputsource是sax中表示资源的对象
  18. InputSource inputSource = new InputSource(inputStream);
  19. //设置字符编码 spring源码中判断逻辑特别多 ,稳定化的框架并不相信一切外部的输入
  20. //这也是软件架构原则中的规范之一 稳定性体现
  21. if (encodedResource.getEncoding() != null) {
  22. inputSource.setEncoding(encodedResource.getEncoding());
  23. }
  24. //真正干活的逻辑 ,加载beanDefinition的入口
  25. return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
  26. }
  27. catch (IOException ex) {
  28. throw new BeanDefinitionStoreException(
  29. "IOException parsing XML document from " + encodedResource.getResource(), ex);
  30. }
  31. finally {
  32. //因为resourcesCurrentlyBeingLoaded表示当前线程正在加载的redource
  33. //执行到这里说明资源已经加载完了或者失败了
  34. //所以需要将当前资源移除出去
  35. currentResources.remove(encodedResource);
  36. //set没有数据了,说明没啥乱用了,清理一下内存 防止threadlocal内存泄漏
  37. if (currentResources.isEmpty()) {
  38. this.resourcesCurrentlyBeingLoaded.remove();
  39. }
  40. }
  41. }

这里使用了一个委派模式,将真正干活的逻辑交给了**doLoadBeanDefinitions()**

    protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
            throws BeanDefinitionStoreException {

        try {
            //把 resource 转换成程序层面可以识别的有层次结构的document对象
            Document doc = doLoadDocument(inputSource, resource);
            //解析文档对象,生成beanDefinition注册到beanFactory中,最终返回新注册到beanFactory的beanDefinition数量
            int count = registerBeanDefinitions(doc, resource);
            //日志打印
            if (logger.isDebugEnabled()) {
                logger.debug("Loaded " + count + " bean definitions from " + resource);
            }
            //返回新注册 bean定义信息的数量
            return count;
        }
        catch (BeanDefinitionStoreException ex) {
            throw ex;
        }
        //这里省略部分catch的逻辑
    }

这里再次分成了两步:

  1. **Resource**转换成**Document****doLoadDocument(inputSource, resource)**
  2. 解析文档对象,生成**beanDefinition**注册到**BeanFactory****registerBeanDefinitions(doc, resource)**

12.Resource转化成Document

**doLoadDocument(inputSource, resource)**

    protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
        //这个方法就是将inputSource转化成可以识别的文档对象
        //通过文档加载器来转化的
        //1. getEntityResolver? 这个实体解析器
        //spring官网说明:如果sax应用程序中需要实现自定义处理外部实体,则必须实现此接口并使用setEntityResolver方法向sax驱动器注册一个实例
        //也就是说,对于解析一个xml,sax首先读取xml文档上的声明,根据声明去寻找相应的DTD/XSD定义,以便对文档进行一个校验。
        //默认的寻找校验规则,即通过网络来下载响应的DTD/XSD声明,在进行校验。并且下载的过程是一个漫长且不可控的过程,当下在失败后,这里还会抛出异常
        //那么有什么办法可以避免直接从网络上下载呢?使用EntityResolver
        //EntityResolver的作用是项目本身可以提供一个如何寻找DTD/XSD声明的方法,即让程序来实现寻找定义声明的过程,比如我们将定义文件
        //放到项目的某个地方,在实现时直接将此文件读取并返回给sax即可,这样避免了通过网络来寻找对应的声明。
        //2.验证模式是怎么获取的?getValidationModeForResource()
        return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
                getValidationModeForResource(resource), isNamespaceAware());
    }

**getValidationModeForResource(resource)**

    protected int getValidationModeForResource(Resource resource) {
        //获取默认的validationMode
        int validationModeToUse = getValidationMode();
        //条件成立:说明set过默认值,一般情况下,不会走这里,都是使用自动检测
        if (validationModeToUse != VALIDATION_AUTO) {
            return validationModeToUse;
        }
        //自动检查xml使用的是哪种验证模式?由这个方法决定
        int detectedMode = detectValidationMode(resource);

        if (detectedMode != VALIDATION_AUTO) {
            return detectedMode;
        }
        // Hmm, we didn't get a clear indication... Let's assume XSD,
        // since apparently no DTD declaration has been found up until
        // detection stopped (before finding the document's root tag).
        return VALIDATION_XSD;
    }

**documentLoader.loadDocument()**

    @Override
    public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
            ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {

        DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
        if (logger.isTraceEnabled()) {
            logger.trace("Using JAXP provider [" + factory.getClass().getName() + "]");
        }
        DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
        return builder.parse(inputSource);
    }

**DocumentBuilder.parse() **就是真正的解析逻辑。

13.解析文档对象注册到BeanFactory

**registerBeanDefinitions()** 解析文档对象,生成beanDefinition注册到beanFactory中,最终返回新注册到beanFactory的beanDefinition数量。

    public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
        //创建一个 BeanDefinitionDocumentReader   一对一处理    每个文档对象都会创建一个 BeanDefinitionDocumentReader 对象
        BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
        //getRegistry 会返回程序创建的beanFactory实例
        //countBefore 解析doc之前,bf中已经有的bd数量
        int countBefore = getRegistry().getBeanDefinitionCount();
        //解析文档,并且注册到bf中
        //xmlReaderContext :包含最主要的参数是当前 this -> xmlBeanDefinitionReader  -> bf
        documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
        //返回值 返回新注册的bd数量    最新的 - 注册之前的
        return getRegistry().getBeanDefinitionCount() - countBefore;
    }

看重载的方法

    @Override
    public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
        //引用上下文对象
        this.readerContext = readerContext;
        //doc.getDocumentElement()  拿出文档代表的xml的顶层标签  <beans></beans>
        /*真正的解析xml的逻辑*/
        doRegisterBeanDefinitions(doc.getDocumentElement());
    }

又是一个委派模式,终于来到了解析xml的逻辑,这里的逻辑其实没有什么可以学习的点,追到这里主要是为了串起来整个解析xml文件的流程,具体解析xml的过程不做重点说明。

    protected void doRegisterBeanDefinitions(Element root) {

        BeanDefinitionParserDelegate parent = this.delegate;
        //方法返回一个beans标签 解析器对象
        this.delegate = createDelegate(getReaderContext(), root, parent);
        //解析器对象去判断是不是默认的命名空间  一般情况下  条件成立
        if (this.delegate.isDefaultNamespace(root)) {
            //获取 profile 属性, 环境  Dev  prod  test
            String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
            //条件成立   说明 beans 标签上 有 profile 属性
            if (StringUtils.hasText(profileSpec)) {
                //将属性值按照,拆分成字符串数组
                String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
                        profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
                // We cannot use Profiles.of(...) since profile expressions are not supported
                // in XML config. See SPR-12458 for details.
                // Environment.acceptsProfiles(String [] args) 条件成立  :说明beans 标签可以继续解析 bd
                //这里取反  ,所以   就是 if里面整个条件成立  ,说明该 beans 标签不在继续解析  ,直接返回
                if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
                    if (logger.isDebugEnabled()) {
                        logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec +
                                "] not matching: " + getReaderContext().getResource());
                    }
                    return;
                }
            }
        }
        //这里是留给子类的扩展点
        preProcessXml(root);
        parseBeanDefinitions(root, this.delegate);
        //这里也是留给子类扩展  体现的软件设计模式的开闭原则
        postProcessXml(root);

        this.delegate = parent;
    }

已经走到了这里,大概看一下里面的逻辑。

**parseBeanDefinitions(root, this.delegate)**

    protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
        /*条件成立说明root是spring缺省的命名空间*/
        if (delegate.isDefaultNamespace(root)) {
            /*这里获取的,大部分情况下,其实都是bean标签*/
            NodeList nl = root.getChildNodes();
            /*迭代处理每一个子标签*/
            for (int i = 0; i < nl.getLength(); i++) {
                Node node = nl.item(i);
                if (node instanceof Element) {
                    Element ele = (Element) node;
                    /*说明子标签也是默认的spring标签*/
                    if (delegate.isDefaultNamespace(ele)) {
                        /*默认标签解析逻辑 step into*/
                        parseDefaultElement(ele, delegate);
                    }/*自定义标签解析逻辑*/
                    else {
                        delegate.parseCustomElement(ele);
                    }
                }
            }
        }/*root不是默认的命名空间,解析自定义标签逻辑*/
        else {
            delegate.parseCustomElement(root);
        }
    }

因为我们没有自定义标签,所以看默认标签的解析逻辑 **parseDefaultElement(ele, delegate)**

    private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
        /*条件成立,说明此时是import标签*/
        if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
            importBeanDefinitionResource(ele);
        }/*条件成立说明是alias别名标签*/
        else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
            processAliasRegistration(ele);
        }/*此时说明是bean标签*/
        else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
            /*解析bean标签*/
            processBeanDefinition(ele, delegate);
        }/*说明是嵌套beans标签*/
        else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
            // 递归到上层重新来了
            doRegisterBeanDefinitions(ele);
        }
    }

�继续看一下bean标签的解析逻辑**processBeanDefinition(ele, delegate)**

    protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
        /*用解析器对象解析标签  hodler里面包含三个属性:beanDefinition,beanName,Alias别名信息*/
        BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
        if (bdHolder != null) {
            /*如果当前hodler需要被装饰,执行装饰逻辑 主要是处理自定义属性*/
            bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
            try {
                // Register the final decorated instance.
                /*注册当前bean倒容器中 通过readerContext拿到XMLBeanDefinition拿到beanFactory*/
                BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
            }
            catch (BeanDefinitionStoreException ex) {
                getReaderContext().error("Failed to register bean definition with name '" +
                        bdHolder.getBeanName() + "'", ele, ex);
            }
            // Send registration event.
            /*发送一个bean注册完成的事件*/
            getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
        }
    }

**BeanDefinitionReaderUtils.registerBeanDefinition()** 单实例bean的注册逻辑。

    public static void registerBeanDefinition(
            BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
            throws BeanDefinitionStoreException {

        // Register bean definition under primary name.
        String beanName = definitionHolder.getBeanName();
        registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

        // Register aliases for bean name, if any.
        String[] aliases = definitionHolder.getAliases();
        if (aliases != null) {
            for (String alias : aliases) {
                registry.registerAlias(beanName, alias);
            }
        }
    }

最终将**beanDefinition**放到了**beanDefinitionMap**里面,至此整个解析xml配置文件,加载**beanDefinition**并返回全新**BeanFactory**的逻辑结束了。