【超详细的Spring源码分析 —— 03 Spring IoC 对于 Bean 管理的核心组件分析 - 解析并注册 BeanDefinition】

537 阅读14分钟

在上一篇文章中,解析前的准备工作已经分析完毕,接着分析解析 BeanDefinition 的逻辑。

beanDefinitionReader.loadBeanDefinitions(resource);

首先我们进入到 XmlBeanDefinitionReader.loadBeanDefinitions(Resource resource)

@Override
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
    // 这里会先对resource封装一些编码、字符集的处理,然后调用一个重载方法
    // 返回值是注册 BeanDefinition 的个数
    return loadBeanDefinitions(new EncodedResource(resource));
}

进入到 XmlBeanDefinitionReader.loadBeanDefinitions(EncodedResource encodedResource)

public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
    Assert.notNull(encodedResource, "EncodedResource must not be null");
    if (logger.isTraceEnabled()) {
        logger.trace("Loading XML bean definitions from " + encodedResource);
    }
    // this.resourcesCurrentlyBeingLoaded是一个ThreadLocal<Set<EncodedResource>>类型的对象
    // 它用于存储当前线程私有的EncodedResource集合
    // 这里会获取到当前线程私有的资源集合
    Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
    // 如果获取到的资源集合Set为null
    if (currentResources == null) {
        // 那么会初始化一个set, 然后将其set到threadLocal中
        currentResources = new HashSet<>(4);
        this.resourcesCurrentlyBeingLoaded.set(currentResources);
    }
    // 尝试将resource资源添加到set, 如果添加失败了则抛出异常
    // Spring提供了 <import> 标签, 用于在xml配置文件中导入其他多个配置文件
    // 如果 A.xml import B.xml, 且 B.xml import A.xml
    // 那么在加载 A.xml 的时候也会加载 B.xml
    // 但 B.xml 是依赖于 A.xml 的, 这就会导致A、B之间的循环导入
    // 为了避免这种情况, 这里通过set来进行一个去重
    // 简单来说, 就是解决资源的循环依赖问题
    if (!currentResources.add(encodedResource)) {
        throw new BeanDefinitionStoreException("Detected cyclic loading of " + encodedResource + " - check your import definitions!");
    }
    // 首先是一个 try-with-resource 语句
    // 通过 Resource 中封装的 ClassLoader 加载了  的输入流
    try (InputStream inputStream = encodedResource.getResource().getInputStream()) {
        // 然后会将inputStream封装为InputSource
        // InputSource封装了xml的解析方式
        // 1. DOM: 将整个xml读取到内存进行解析
        // 2. SAX: 以文件流的形式逐行加载到内存并解析
        // Spring采用的是后者
        InputSource inputSource = new InputSource(inputStream);
        // 如果encodedResource被设置了编码格式, 那么在解析的时候采用指定编码格式
        if (encodedResource.getEncoding() != null) {
            inputSource.setEncoding(encodedResource.getEncoding());
        }
        // 然后就是真正的解析逻辑了
        // 这里会调用 doLoadBeanDefinitions, 传入一个InputSource、以及 resource
        return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
    } catch (IOException ex) {
        throw new BeanDefinitionStoreException("IOException parsing XML document from " + encodedResource.getResource(), ex);
    } finally {
        // 为了避免内存泄漏
        // 在资源被加载完毕后, 会在Set集合中删除掉resource
        currentResources.remove(encodedResource);
        // 并且当资源集合为空时, 删除掉threadLocal中的元素
        if (currentResources.isEmpty()) {
            this.resourcesCurrentlyBeingLoaded.remove();
        }
    }
}

接着深入到 XmlBeanDefinitionReader.doLoadBeanDefinitions(InputSource inputSource, Resource resource)

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException {
    try {
     	// 通过sax解析xml资源, 获取到Document对象
        Document doc = doLoadDocument(inputSource, resource);
     	// 进行文档解析, 然后注册Bean实例到工厂中
        int count = registerBeanDefinitions(doc, resource);
        if (logger.isDebugEnabled()) {
            logger.debug("Loaded " + count + " bean definitions from " + resource);
        }
        // 返回解析并注册的bean个数
        return count;
    } 
    // catch 省略
}

深入 XmlBeanDefinitionReader.registerBeanDefinitions(Document doc, Resource resource)

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
   // 创建文档读取器
   // 这个文档读取器就会负责bean的解析以及注册工作
   // 我们会在下方单独把这个方法拎出来看看内部的逻辑
   BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
   // 获取到解析resource之前的, 容器中的bean的数量
   int countBefore = getRegistry().getBeanDefinitionCount();
   // 创建一个读取器上下文作为参数
   // 调用文档读取器解析doc并注册BeanDefinition
   documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
   // 返回当前注册的Bean个数
   // 如果是在 xml中import了多个文档, 那么在第1次注册动作以后, 容器中就有可能存在之前已经注册的Bean实例
   // 因此这里需要减去之前已经存在的BeanDifinition个数
   return getRegistry().getBeanDefinitionCount() - countBefore;
}

上面这个方法中涉及到几个比较关键的组件,我们依次来深入看看:

1. XmlBeanDefinitionReader.createBeanDefinitionDocumentReader()

protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() {
    // 这里调用了一个工具类来返回读取器实例
    // 传入的参数就是全局变量中的documentReaderClass
    // 底层通过反射来获取this.documentReaderClass这个引用指向的Class对象的实例
    return BeanUtils.instantiateClass(this.documentReaderClass);
}

我们有必要对参数 this.documentReaderClass 进行一个简单的了解。

documentReaderClassXmlBeanDefinitionReader 这个类中声明的全局变量,它默认指向 DefaultBeanDefinitionDocumentReader.class 。一般情况下,我们都会采用 DefaultBeanDefinitionDocumentReader 的实例来作为Document读取器。

我们接着去看一下 DefaultBeanDefinitionDocumentReader 内部封装的属性:

public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocumentReader {

    // <bean> 标签下使用所有的元素都被封装在这里
    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";

    // 读取器上下文
    @Nullable
    private XmlReaderContext readerContext;

    // 真正执行解析逻辑的委托实例
    @Nullable
    private BeanDefinitionParserDelegate delegate;
}

可以发现,Spring配置文件中能够使用的所有标签以及属性都在这个类中被定义了。并且内部还封装了两个比较关键的组件:读取器上下文、Bean定义解析器委托。

提前剧透一下,Spring采用了委托模式,真正执行 BeanDefinition 解析工作的并非是 DefaultBeanDefinitionDocumentReader,而是它内部封装的解析器委托。

2. createReaderContext(Resource)

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

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

回到 XmlBeanDefinitionReader.registerBeanDefinitions(Document doc, Resource resource) ,我们继续来看解析的流程:

深入到 DefaultBeanDefinitionDocumentReader.registerBeanDefinitions(Document doc, XmlReaderContext readerContext)

@Override
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
    // 将读取器上下文对象赋值到 DefaultBeanDefinitionDocumentReader 的全局变量中
    // 它会在后续用于构建解析器委托的参数
    this.readerContext = readerContext;
    // 获取到Document对象中的具体元素
    // 并通过这个元素开始解析并注册文档对象中的BeanDefinition
    doRegisterBeanDefinitions(doc.getDocumentElement());
}

深入到 DefaultBeanDefinitionDocumentReader.doRegisterBeanDefinitions(Element root)

@SuppressWarnings("deprecation")
protected void doRegisterBeanDefinitions(Element root) {
    // 以下是Spring的原生注解翻译:
    //      任何嵌套的<beans>标签都会导致这个方法的递归调用
    //      就比如我们通过在xml中 <import> 了另一个xml, 那么就会先递归处理另一个xml文档
    
    // 这里也非常关键
    // 在 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);
            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;
            }
        }
    }

    // 这里采用了模板方法设计模式, 当我们需要对DefaultBeanDefinitionDocumentReader进行一些扩展
    // 那么只需要继承, 并重写preProcessXml、preProcessXml就能够实现这个方法的扩展
    // 执行的顺序是由DefaultBeanDefinitionDocumentReader指定, 子类只需要负责扩展即可。
    
    // 默认空实现, 用于在解析文档之前做一些预处理
    preProcessXml(root);
    
    // 具体解析流程, 委托设计模式, 将具体的解析交给 this.delegate 来完成
    parseBeanDefinitions(root, this.delegate);
    
    // 默认空实现, 用于在解析文档之后做一些预处理
    preProcessXml(root);
    
    this.delegate = parent;
}

通过 parseBeanDefinitions(root, this.delegate);,我们能够明确,真正执行解析逻辑的并不是 DefaultBeanDefinitionDocumentReader 这个读取器,而是这个读取器内部封装的 BeanDefinitionParserDelegate。delegate 实例的内部结构如下图所示, Resource、XmlBeanDefinition、DefaultListableBeanFactory 都被封装在内了。

深入 DefaultBeanDefinitionDocumentReader.parseBeanDefinitions(Element root, BeanDefinitionParserDelegate 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)) { 
                    // 通过delegate去解析这个ele元素
                    parseDefaultElement(ele, delegate); 
                }
                else { // 若不是默认命名空间的元素, 则以客制化的方式去解析节点
                    delegate.parseCustomElement(ele);
                }
            }
    	}
    }
    else { // 若root不是默认命名空间的元素, 则以客制化的方式去解析节点
    	delegate.parseCustomElement(root);
    }
}

深入 DefaultBeanDefinitionDocumentReader.parseDefaultElement(Element ele, BeanDefinitionParserDelegate 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, 就通过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(Element ele, BeanDefinitionParserDelegate delegate);

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
    // 通过delegate解析bean定义, 获取到一个BeanDefinitionHolder实例
    BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
    
    // 如果bdHolder不为空, 说明解析成功了, 之后就是注册环节了
    if (bdHolder != null) {
        // 装饰bdHolder, 如果需要的话
    	bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
    	try {
            // 通过读取器上下文获取到 registry, 进行注册操作
            BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
    	}
    	catch (BeanDefinitionStoreException ex) {
            getReaderContext().error("Failed to register bean definition with name '" + bdHolder.getBeanName() + "'", ele, ex);
    	}
    	// 整个解析流程完成后, 发布事件广播
    	getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
    }
}

这个方法具备两个核心流程,解析 BeanDefinition、注册 BeanDefinition 到工厂中,我们逐个分析内部的流程。

1. 解析 BeanDefinition

回到

深入 BeanDefinitionParserDelegate.parseBeanDefinitionElement(Element ele)

@Nullable
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
    // 调用一个重载方法
    return parseBeanDefinitionElement(ele, null);
}

深入 BeanDefinitionParserDelegate.parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean)

@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, 这个Bean定义已经注入上了解析的所有属性
    // 比如全限定类名、属性列表、scope等等
    // 一会儿我们会在下方详细分析这个逻辑
    AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
    
    // 如果我们解析出来的bean Definition不为空
    if (beanDefinition != null) {
        // 若未指定唯一标识, 则会自动生成一个beanName
    	if (!StringUtils.hasText(beanName)) {
            // 省略掉自动创建beanName的逻辑
    	}
    	// 将别名数组转换为String数组
    	String[] aliasesArray = StringUtils.toStringArray(aliases);
    	// 这里会通过beanDefinition、beanName、别名数组来生成一个 BeanDefinitionHolder
    	// 这个beanName就是对应beanDefinition的一个映射
    	return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
    }
    return null;    
}

深入 BeanDefinitionParserDelegate.parseBeanDefinitionElement(Element ele, String beanName, @Nullable BeanDefinition containingBean)

@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定义
        // 这个实例中的所有属性(除了class)都是为空或默认的
    	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块
    return null;
}

深入 createBeanDefinition(className, parent)

protected AbstractBeanDefinition createBeanDefinition(@Nullable String className, @Nullable String parentName)
		throws ClassNotFoundException {
    // 调用工具类提供的方法, 获取到AbstractBeanDefinition实例
    // 传入的参数有一个父类的全限定类名、待解析BeanDefinition的全限定类名、以及一个类加载器
    return BeanDefinitionReaderUtils.createBeanDefinition(parentName, className, this.readerContext.getBeanClassLoader());
}

深入 BeanDefinitionReaderUtils.createBeanDefinition(@Nullable String parentName, @Nullable String className, @Nullable ClassLoader classLoader)

public static AbstractBeanDefinition createBeanDefinition(@Nullable String parentName, @Nullable String className, @Nullable ClassLoader classLoader) throws ClassNotFoundException {
    // 这个GenericBeanDefinition实例会封装:待实例化的Bean的Class对象,或者是待实例化的Bean的全限定类名
    // 如果 ClassLoader 不为空, 就会封装一个 Class 对象
    // 不然就封装一个全限定类名
    GenericBeanDefinition bd = new GenericBeanDefinition();
    
    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;
}

当这个方法执行完成后,BeanDefinition 就已经被解析完毕了,它包含了我们再配置文件中定义的各种信息,比如属性列表、scope、init-method、全限定类名等等。

后续我们还需要将这个 BeanDefinition 注册到工厂中,因此我们继续分析注册流程。

2. 注册 BeanDefinition

回到 DefaultBeanDefinitionDocumentReader.processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate); 这个方法中,继续深入 BeanDefinitionReaderUtils.registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry);

public static void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException {
    // 获取到Bean的唯一标识
    String beanName = definitionHolder.getBeanName();
    // 将 bean 定义真正注册到 Bean 工厂中
    registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
    
    // 如果存在别名, 那么会注册别名
    String[] aliases = definitionHolder.getAliases();
    if (aliases != null) {
    	for (String alias : aliases) {
            registry.registerAlias(beanName, alias);
    	}
    }
}

深入 DefaultListableBeanFactory.registerBeanDefinition(String beanName, BeanDefinition beanDefinition)

@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException {
    Assert.hasText(beanName, "Bean name must not be empty");
    Assert.notNull(beanDefinition, "BeanDefinition must not be null");
    
    // 如果Bean定义是 AbstractBeanDefinition 类型, 那么会进行一个验证逻辑
    if (beanDefinition instanceof AbstractBeanDefinition) {
    	try {
            ((AbstractBeanDefinition) beanDefinition).validate();
    	}
    	catch (BeanDefinitionValidationException ex) {
            throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, "Validation of bean definition failed", ex);
    	}
    }
    
    // 先尝试从工厂中获取一下我们要注册的bean定义(通过beanName)
    // 看看工厂中是否已经存在了 beanName 对应的 BeanDefinition
    BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
    // 如果存在
    if (existingDefinition != null) {
        // 那么为判断是否允许 BeanDefinition 的覆盖
        // 如果不允许就抛出异常
    	// 一般都是不允许的
    	if (!isAllowBeanDefinitionOverriding()) {
            throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
    	}
    	// 如果允许覆盖, 那么会根据不同情况进行日志的打印
        // 省略日志打印的逻辑
    	else if (existingDefinition.getRole() < beanDefinition.getRole()) {
            if (logger.isInfoEnabled()) {
                // ...	
            }
    	}
    	else if (!beanDefinition.equals(existingDefinition)) {
            if (logger.isDebugEnabled()) {
            	// ...
            }
    	} 
    	else { 
            if (logger.isTraceEnabled()) {
            	// ...
    	}
    	// 进行一个bean定义的覆盖,放置key-value到map中
    	this.beanDefinitionMap.put(beanName, beanDefinition);
    }
    else { // 如果 concurrentHashMap 中不存在相应的bean定义键值对
    	// 那么会检查这个工厂中 bean 的创建阶段是否已经启动
        // 底层就是查看 alreadyCreated 这个容器是否为空
        // 若不为空, 说明已经开始创建Bean实例了
    	if (hasBeanCreationStarted()) {
            // 这种情况下会进行一个同步
            // 防止多个线程同时进行注册的操作
            synchronized (this.beanDefinitionMap) {
            	this.beanDefinitionMap.put(beanName, beanDefinition);
            	List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
            	updatedDefinitions.addAll(this.beanDefinitionNames);
            	updatedDefinitions.add(beanName);
            	this.beanDefinitionNames = updatedDefinitions;
            	removeManualSingletonName(beanName);
            }
    	}
    	else { // 否则就会直接注册, 不需要进行同步
            // 添加beanDefinition到beanDefinitionMap这个map中
            this.beanDefinitionMap.put(beanName, beanDefinition);
            // 添加容器中存在的BeanDefinitionName到beanDefinitionNames这个数组中
            this.beanDefinitionNames.add(beanName);
            removeManualSingletonName(beanName);
    	}
    	this.frozenBeanDefinitionNames = null;
    }
    
    if (existingDefinition != null || containsSingleton(beanName)) {
        // 重置 BeanDefinition 缓存的处理
    	resetBeanDefinition(beanName);
    }
}

当上面这个方法执行结束后,基本上整个 "解析并注册BeanDefinition" 的流程就结束了。

下方是一个方法调用栈图示,或许能够更清晰地表达出代码的调用过程。

简单来说,beanDefinitionReader.loadBeanDefinitions(resource); 这个方法主要执行了如下几个步骤:

  • 解析 xml, 获取到对应的 Document 实例。

  • 创建 BeanDefinitionDocumentReader,它内部封装了具体用于解析的委托 delegate。

  • 通过 delegate 去解析文档节点, 并根据 xml 中配置的属性生成相应的 BeanDefinition。

  • 将 BeanDefinition 注册到 Registry 工厂中。

下一章具体分析 Bean 的实例化过程。