【超详细的Spring源码分析 —— 04 Spring对于Bean管理的核心组件源码分析 - 解析Bean Definition】

这一篇的内容至关多,而且逻辑也会相对复杂,我尽量描述清楚 Spring 在解析 Bean Definition 时完成的工做。java

在这一篇中,分析的代码入口是下面这个方法开始:node

doLoadBeanDefinitions(inputSource, encodedResource.getResource());
复制代码

若是不清楚这个方法具体在哪一个位置去执行,能够去看上一篇文章。spring

分析以前我先提醒下各位,必需要对以前咱们作的那些准备工做有一个清晰的概念,由于不少的组件就是在这里被使用的。express

1、解析的入口

/** * 从指定xml配置文件中加载出Bean实例 * @param inputSource 以sax的形式进行读取资源 * @param resource xml配置文件的资源描述 * @return 解析的bean个数 */
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException {
    try {
     	// 这里会获取到当前xml文件对应的Document对象
     	// 这个Document对象是一个xml的标准
     	// 简单来讲, 它就表明了咱们传入的xml配置文件
     	// 它须要的两个参数:
     	// 1. inputSource:内部封装了配置文件的文件流以及定义的解析方式
     	// 2. resource:内部封装了path路径以及解析器(ClassLoader或Class实例)
     	// 别忘了inputSource就是经过resource来读取的输入流
     	// 其实咱们真正用到resource的, 也就是它内部的一个解析器
        Document doc = doLoadDocument(inputSource, resource);
        
        // 这个方法是关键中的关键!!!!!!!!!!!!!!!!!
     	// 进行文档解析, 而后注册Bean实例到工厂中
     	// 而后会返回一个解析并注册的Bean的个数
        // 我会在下方详细分析这个方法
        int count = registerBeanDefinitions(doc, resource);
        // 若是debug开启, 记录日志
        if (logger.isDebugEnabled()) {
            logger.debug("Loaded " + count + " bean definitions from " + resource);
        }
        // 返回解析并注册的bean个数
        return count;
    // 下面的逻辑就是捕获并抛出相应的异常了
    // 咱们只须要注意, 异常的范围应该从小到大, 而且应该尽量细粒度化
    // 从这一点来看, Spring也完成的至关好
    } 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);
    }
}
复制代码

2、真真正正的解析 —— registerBeanDefinitions()

在分析以前,咱们先看一下这个方法须要提供的参数:设计模式

  • doc 文档对象
  • resource 资源对象

它们之间的关系咱们须要先有一个清楚的认知:数组

  1. 咱们先经过资源对象中的 path 去读取对应位置的 xml 配置文件的文件流,并封装 sax 解析规则,生成了一个inputSource。
  2. 而后又经过 inputSource 与 resource,获取到了xml配置文件对应的文档对象

而后这个 registerBeanDefinitions() 方法,它又须要提供文档对象以及 resource 实例。有没有发现,自始至终,这个 resource 它都阴魂不散。bash

那么 resource 做为准备阶段就构建好的组件,从如今开始,咱们最依赖它的,将会是它内部的解析器(ClassLoader/Class)。ide

/** * 注册文档中包含的Bean定义 * 返回解析并注册到工厂的bean个数 */
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
   // 建立文档读取器
   // 这个文档读取器就会负责bean的解析以及注册工做
   // 咱们会在下方单独把这个方法拎出来看看内部的逻辑
   BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
   // 获取到解析resource以前的, 容器中的bean的数量
   int countBefore = getRegistry().getBeanDefinitionCount();
   // 这个方法是整个注册bean到容器的核心逻辑
   // 调用文档读取器去解析并注册Bean
   documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
   // 返回当前注册的Bean个数
   // 这里提一下:
   // 若是是在 xml中import了多个文档, 那么在第1次注册动做之后, 容器中就有可能存在以前已经注册的Bean实例
   // 所以这里须要减去以前已经存在的BeanDifinition个数
   return getRegistry().getBeanDefinitionCount() - countBefore;
}
复制代码

这个方法完成的核心逻辑有两步:工具

  1. 建立 BeanDefinitionDocumentReader,也就是Bean定义文档读取器,它将负责从文档对象中读取出Bean Definition。
  2. 经过文档读取器去解析并注册Bean Definition

下面咱们依次详细分析这两个步骤:ui

3、文档读取器的建立 —— createBeanDefinitionDocumentReader();

XmlBeanDefinitionReader.createBeanDefinitionDocumentReader()

/** * 建立一个文档读取器, 用于读取文档中的Bean定义 */
protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() {
    // 这里调用了一个工具类来返回读取器实例
    // 传入的参数就是全局变量中的 documentReaderClass
    // 底层就是经过反射来获取this.documentReaderClass这个引用指向的Class对象的实例
    return BeanUtils.instantiateClass(this.documentReaderClass);
}
复制代码

工具类 BeanUtils.instantiateClass() 这个方法顾名思义,就是实例化指定的Class对象。所以咱们有必要对参数 this.documentReaderClass 进行一个简单的了解。

documentReaderClass 是 XmlBeanDefinitionReader 这个类中声明的全局变量,它默认指向一个DefaultBean定义读取器的Class类。通常状况下,咱们都会采用这个类的实例对象来做为一个读取器。

private Class<? extends BeanDefinitionDocumentReader> documentReaderClass =
			DefaultBeanDefinitionDocumentReader.class;
复制代码

接着去粗略地看一下 DefaultBeanDefinitionDocumentReader 这个类

// 能够发现, 该类包含了不少Spring配置文件中的标签元素
// 实际上, 咱们在Spring配置文件中,可以使用的全部标签以及属性都在这个类中被定义了
public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocumentReader {

    public static final String BEAN_ELEMENT = BeanDefinitionParserDelegate.BEAN_ELEMENT;
    
    public static final String NESTED_BEANS_ELEMENT = "beans";
    
    public static final String ALIAS_ELEMENT = "alias";
    
    public static final String NAME_ATTRIBUTE = "name";
    
    public static final String ALIAS_ATTRIBUTE = "alias";
    
    public static final String IMPORT_ELEMENT = "import";
    
    public static final String RESOURCE_ATTRIBUTE = "resource";
    
    public static final String PROFILE_ATTRIBUTE = "profile";
    
    // 只是简单了解, 所以忽略掉剩余的代码....
}
复制代码

这边我省略掉了 DefaultBeanDefinitionDocumentReader 这个类的大部分代码,其实它还提供了诸多解析相关的API,感兴趣能够提早去本身看一下。

那么咱们继续分析下一个核心步骤 —— 经过文档读取器去解析并注册Bean Definition。

4、经过文档读取器来解析并注册Bean

咱们先看一下执行的代码:

documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
复制代码

能够发现,这个方法须要传递文档对象,以及一个读取器上下文。

那么首先咱们能够简单先看一下读取器上下文相关的代码:

/** * 建立一个XML读取器上下文 */
public XmlReaderContext createReaderContext(Resource resource) {
	// 调用了一个 XML读取器上下文的构造方法
	// 传递了诸多的参数, 包括资源、错误报告器、事件监听器、命名空间解析器等
	return new XmlReaderContext(resource, this.problemReporter, this.eventListener,
			this.sourceExtractor, this, getNamespaceHandlerResolver());
}
复制代码

咱们再接着深刻到 XmlReaderContext 这个构造方法中:

public XmlReaderContext( Resource resource, ProblemReporter problemReporter, ReaderEventListener eventListener, SourceExtractor sourceExtractor, XmlBeanDefinitionReader reader, NamespaceHandlerResolver namespaceHandlerResolver) {
    super(resource, problemReporter, eventListener, sourceExtractor);
    this.reader = reader;
    this.namespaceHandlerResolver = namespaceHandlerResolver;
}
复制代码

可以发现,读取器上下文内部还封装了XmlBeanDefinitionReader这个读取器,所以咱们能够简单地将 XmlReaderContext 理解为 “XmlBean定义读取器的一个扩展” 。

ok,回到 registerBeanDefinitions() 相关的代码:

DefaultBeanDefinitionDocumentReader.registerBeanDefinitions()

@Override
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
    // 将读取器上下文对象赋值到文档读取器的全局变量中
    this.readerContext = readerContext;
    // 获取到Document对象中的具体元素
    // 并经过这个元素开始解析并注册文档对象中的BeanDefinition
    doRegisterBeanDefinitions(doc.getDocumentElement());
}
复制代码

DefaultBeanDefinitionDocumentReader.doRegisterBeanDefinitions()

/** * 经过给定的root元素, 注册该元素下每个的Bean Definition * 这里的root就是咱们在xml中定义的 <beans> 这个标签 */
@SuppressWarnings("deprecation")
protected void doRegisterBeanDefinitions(Element root) {
    // 如下是Spring的原生注解翻译:
    // 任何嵌套的<beans>标签都会致使这个方法的递归调用
    // 就好比咱们经过在xml中 <import> 了另外一个xml, 那么就会先递归处理另外一个xml文档
    // 而且要记得, 在解析前的准备阶段, Spring是已经处理完配置文件的循环依赖问题的
    
    // 这里也很是关键
    // 在 DefaultBeanDefinitionDocumentReader 中有一个全局变量 delegate
    // 这个 delegate 才是真正负责解析的组件
    // 这里其实就采用了委托模式
    // DefaultBeanDefinitionDocumentReader 将具体的解析动做委托给了 delegate 完成
    // 可是这个 delegate 还未被初始化
    // 所以后续还会有一个初始化的流程
    // 这里就先把delegate赋值给了一个BeanDefinitionParserDelegate类型的引用
    // 这个parent引用其实主要是用于delegate初始化时处理继承关系
    BeanDefinitionParserDelegate parent = this.delegate;
    
    // 这里就是具体建立 delegate 的逻辑了
    this.delegate = createDelegate(getReaderContext(), root, parent);
    
    // 若是 delegate 是采用默认命名空间
    if (this.delegate.isDefaultNamespace(root)) {
        // 获取到 profile 属性
        String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
        // 处理 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.
            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;
            }
        }
    }
    // 前面的工做都至关于一个解析Bean以前的预处理, 后面才是解析Bean的逻辑
    
    // 这里采用了模板方法设计模式, 当咱们须要对DefaultBeanDefinitionDocumentReader进行一些扩展
    // 那么只须要继承, 并重写preProcessXml、preProcessXml就可以实现这个方法的扩展
    // 执行的顺序是由DefaultBeanDefinitionDocumentReader指定, 子类只须要负责扩展便可。
    
    // 默认空实现, 用于在解析文档以前作一些预处理
    preProcessXml(root);
    
    // 具体解析流程, 委托设计模式, 将具体的解析交给 this.delegate 来完成
    parseBeanDefinitions(root, this.delegate);
    
    // 默认空实现, 用于在解析文档以后作一些预处理
    preProcessXml(root);
    
    this.delegate = parent;
}
复制代码

DefaultBeanDefinitionDocumentReader.parseBeanDefinitions();

这个方法涉及到 xml 解析相关的内容了。

简单来讲,就是遍历获取到根节点下,每个子标签中的内容,而后进行解析。同时对那些不处于默认命名空间的子标签进行一个客制化的解析。

/** * 解析root元素下的的 Bean Definition * 解析的标签包含了: "import", "alias", "bean" * 就像我以前说的, 具体的解析是委托给了delegate */
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
    
    if (delegate.isDefaultNamespace(root)) { // 解析root是不是默认命名空间当中的元素
    	NodeList nl = root.getChildNodes(); // 获取到根节点root下的第一层子节点
    	for (int i = 0; i < nl.getLength(); i++) { // 遍历获取每个节点
            Node node = nl.item(i);
            // 判断node是不是一个Element
            // 这里主要是忽略掉注释的解析
            if (node instanceof Element) {
                Element ele = (Element) node;
                if (delegate.isDefaultNamespace(ele)) { // 判断给定节点是不是默认的命名空间
                    
                    //重点!!!
                    parseDefaultElement(ele, delegate); // 经过delegate去解析这个ele元素
                }
                else { // 若不是默认命名空间的元素, 则以客制化的方式去解析节点
                    delegate.parseCustomElement(ele);
                }
            }
    	}
    }
    else { // 若root不是默认命名空间的元素, 则以客制化的方式去解析节点
    	delegate.parseCustomElement(root);
    }
}
复制代码

整个方法中,咱们须要重点关注的是 parseDefaultElement(ele, delegate); ,这个方法会去解析出 Bean Definition。

咱们接着看 parseDefaultElement() 的源码:

DefaultBeanDefinitionDocumentReader.parseDefaultElement()

这个方法简单来讲,就是根据不一样的子标签,好比<bean><import>等,进行不一样的解析处理。

咱们重点关注节点名称为 <bean> 的解析流程:

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, 就经过delegate处理
    else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
    	processBeanDefinition(ele, delegate);
    }
    // 若是节点名还包含了<beans>
    // 也就是 <beans> 中包含了 <beans>
    // 那么就会进行一个 doRegisterBeanDefinitions() 方法的递归调用
    else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
    	// recurse
    	doRegisterBeanDefinitions(ele);
    }
}
复制代码

DefaultBeanDefinitionDocumentReader.processBeanDefinition()

这个方法能够说是本章的核心部分了,它包含了解析、装饰、注册 BeanDefinition 到工厂的入口。

/** * 经过解析器delegate去处理给定的bean element, 并解析出相应的bean Definition, 注册到工厂中 */
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
    // 经过delegate解析bean定义, 获取到一个BeanDefinitionHolder实例
    // 这个实例, 其实就表明了xml文件中的一个完整的bean标签对应的Bean Definition
    BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
    
    // 若是bdHolder不为空, 说明解析成功了, 以后就是注册环节了
    // 注册环节咱们留着在下一篇中详细展开
    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));
    }
}
复制代码

咱们在本章不会涉及到 "注册 BeanDefinition 到工厂的部分",重点关注解析 Bean 的流程。

至于装饰这一块:bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);

感兴趣的话能够本身去了解一下,内容主要就是对客制化的标签进行一些处理,将内容装饰到 BeanDefinition 上。

5、具体的解析Bean Definition流程

这个流程咱们经过这段代码进行展开:

BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
复制代码

首先,深刻到 parseBeanDefinitionElement() 这个方法:

/** * Parses the supplied {@code <bean>} element. May return {@code null} * if there were errors during parse. Errors are reported to the * {@link org.springframework.beans.factory.parsing.ProblemReporter}. */
@Nullable
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
    return parseBeanDefinitionElement(ele, null);
}
复制代码

这里又调用了一个重载的 parseBeanDefinitionElement() 方法

BeanDefinitionParserDelegate.parseBeanDefinitionElement()

这个方法会传入两个参数:

  • Element节点元素
  • 包含的bean Definition

它完成核心逻辑就是:对Element节点进行一系列的解析,获取到一个包含了惟一标识,别名,以及属性,声明周期等等属性的 BeanDefinitionHolder。

须要注意的是,这个 BeanDefinitionHolder 尚未包含被实例化的 Bean 实例。

@Nullable
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
    String id = ele.getAttribute(ID_ATTRIBUTE); // 获取到id属性
    String nameAttr = ele.getAttribute(NAME_ATTRIBUTE); // 获取到name属性, 通常咱们不会设置这个属性
    
    List<String> aliases = new ArrayList<>();
    // 若是存在name属性, 会对name进行相应的字符串处理, 并添加到aliases数组中
    if (StringUtils.hasLength(nameAttr)) {
    	String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
    	aliases.addAll(Arrays.asList(nameArr));
    }
    
    String beanName = id; // 将id做为字符串beanName
    // 若是未指定id, 而且别名数组不为空
    if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
    	// 就会从数组中移除第一个元素做为beanName
    	beanName = aliases.remove(0);
    	if (logger.isTraceEnabled()) {
    		logger.trace("No XML 'id' specified - using '" + beanName +
    				"' as bean name and " + aliases + " as aliases");
    	}
    }
    
    // 检查beanName的惟一性
    if (containingBean == null) {
        checkNameUniqueness(beanName, aliases, ele);
    }
    // 获取到bean Definition
    // 这是个关键的方法, 一下子咱们会在下方详细分析
    AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
    // 若是咱们解析出来的bean Definition不为空
    if (beanDefinition != null) {
        // 但beanName这个表明bean惟一标识的字符串并未指定
    	if (!StringUtils.hasText(beanName)) {
            try { // 下面就是bean惟一标识生成的一些逻辑
            	if (containingBean != null) {
                    beanName = BeanDefinitionReaderUtils.generateBeanName(beanDefinition, this.readerContext.getRegistry(), true);
            	}
            	else { 
            	    // 经过读取器上下文生成一个beanName
                    beanName = this.readerContext.generateBeanName(beanDefinition);
                    // 获取到Bean的ClassName, 全限定类目
                    String beanClassName = beanDefinition.getBeanClassName();
                    // 别名的处理逻辑
                    if (beanClassName != null && beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() && !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
                    	aliases.add(beanClassName);
                    }
            	}
            	if (logger.isTraceEnabled()) {
                    logger.trace("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);
    	
    	// 这里会经过beanDefinition、beanName、别名数组来生成一个 BeanDefinitionHolder
    	// 这个beanName就是对应beanDefinition的一个映射
    	// 能够认为他俩是一个key-value关系
    	return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
    }
    return null;    
}
复制代码

BeanDefinitionParserDelegate.parseBeanDefinitionElement()

这个方法的核心逻辑就是对 Element 进行一系列的处理,好比给返回的 beanDefinition 设置上 property 属性、做用域 scope 等。

/** * 在不关联惟一标识、别名、containingBean这些属性的状况下, 解析出bean definition, * 若解析过程当中出现异常, 会返回null */
@Nullable
public AbstractBeanDefinition parseBeanDefinitionElement(Element ele, String beanName, @Nullable BeanDefinition containingBean) {

    this.parseState.push(new BeanEntry(beanName));
    
    //获取全限定类目
    String className = null;
    if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
    	className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
    }
    
    // 获取bean中定义的父类相关的属性
    String parent = null;
    if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
    	parent = ele.getAttribute(PARENT_ATTRIBUTE);
    }
    try {
        // 根据全限定类名以及父类, 建立一个抽象的Bean定义
    	AbstractBeanDefinition bd = createBeanDefinition(className, parent);
        
        // 解析Bean定义的属性, 其实就是一堆if语句块
        // 判断<bean> 标签中是否存在指定的属性, 若是存在, 那么给db实例set上相应的属性
        // 好比做用域scope、又好比初始化前的预处理init_method等等
    	parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
    	
    	// 设置描述信息
    	bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
    
        // 解析相应的元数据信息, 并set到db中
    	parseMetaElements(ele, bd);
    	
    	// 解析查询的被复写的子元素的信息
    	parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
    	// 解析可替换的方法的子元素的信息
    	parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
        
        // 解析构造方法参数
        // 好比咱们经过<construct-arg>实现bean属性注入, 就是在这里进行解析
    	parseConstructorArgElements(ele, bd);
    	
    	// 解析属性参数
    	// 好比咱们经过<property>实现bean的属性注入, 就在这里解析
    	parsePropertyElements(ele, bd);
    	
    	// 解析Qualifier属性参数
    	parseQualifierElements(ele, bd);
    
        // set上一些资源相关的信息
    	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;
}

复制代码

接下来咱们深刻到这段代码中:AbstractBeanDefinition bd = createBeanDefinition(className, parent);

/** * 根据传入的全限定类名以及父类的全限定类名, 建立出一个抽象Bean定义 */
protected AbstractBeanDefinition createBeanDefinition(@Nullable String className, @Nullable String parentName) throws ClassNotFoundException {
    // 这边会调用一个工具类提供的方法, 获取到AbstractBeanDefinition实例
    // 在这个方法中额外添加了一个类加载器
    // 回忆一下咱们一开始建立的Resource实例, 它是建立好了一个解析器的(Class对象或者是ClassLoader)
    // 而后根据这个resource, 咱们又建立了XML读取器上下文
    // 也就是说, 这个读取器上下文内部的一个解析器, 就是最开始resource中准备好的那个解析器
    return BeanDefinitionReaderUtils.createBeanDefinition(parentName, className, this.readerContext.getBeanClassLoader());
}
复制代码

BeanDefinitionReaderUtils.createBeanDefinition()

经过这个方法,就可以建立出一个抽象的 BeanDefinition,这个 BeanDefinition 会包含待实例化 Bean 的 Class 对象或全限定类名。

public static AbstractBeanDefinition createBeanDefinition(@Nullable String parentName, @Nullable String className, @Nullable ClassLoader classLoader) throws ClassNotFoundException {
    // 这个GenericBeanDefinition实例会封装:待实例化的Bean的Class对象,或者是待实例化的Bean的全限定类名
    GenericBeanDefinition bd = new GenericBeanDefinition();
    
    // 首先set上父类的name
    bd.setParentName(parentName);
    if (className != null) { // 若是全限定类名不为空
    	if (classLoader != null) { // 而且类加载器不为空
    	    // 那么就会经过ClassUtils.forName() 以反射的方式来获取到一个Class类型的实例
    	    // 以后把Class对象给set到GenericBeanDefinition实例上
            bd.setBeanClass(ClassUtils.forName(className, classLoader));
    	}
    	else { // 若是类加载器为空, 那么只设置上全限定类名
    	    // 其实setBeanClass、setBeanClassName赋的全局变量都是通一个Object类型 —— beanClass
    	    // 这个 beanClass 既能够接受一个字符串类型的参数(全限定类名)、又能够接受一个Class类型的参数
            bd.setBeanClassName(className);
    	}
    }
    return bd;
}
复制代码

6、总结

该篇文章主要涉及的是解析 BeanDefinition 的相关逻辑,由于逻辑层次比较复杂,这里我采用图片的形式进行一个全流程的总结:

在下一篇,将带来注册BeanDefinition到容器的具体分析。

相关文章
相关标签/搜索