前言
Spring中的标签包括默认标签和自定义标签两种,而两种标签的用法以及解析方式存在着很大的不同。本篇文章主要分析默认标签的解析。
默认标签的解析是在parseDefaultElement方法中进行。
| 
 1  | 
 // DefaultBeanDefinitionDocumentReader.java private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {  | 
|---|
在以上四种标签的解析中,对bean标签的解析是最复杂的,也是最重要的。本篇文章就重点对bean标签的解析做一些分析。
1 总体流程
processBeanDefinition方法功能就是对bean标签就行解析,源码如下:
| 
 1  | 
 // DefaultBeanDefinitionDocumentReader.java protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {  | 
|---|
上面方法的主要步骤为:
- 调用 
BeanDefinitionParserDelegate类的parseBeanDefinitionElement(Element ele, BeanDefinitionParserDelegate delegate)方法,进行元素解析。 
- 如果解析失败,则返回 
null,错误由ProblemReporter处理。 - 如果解析成功,则返回 
BeanDefinitionHolder实例bdHolder。bdHolder实例已经包含配置文件中配置的各种属性了,例如class、name、id、alias之类的属性。 
- 若实例 
bdHolder不为空,则调用BeanDefinitionParserDelegate类的decorateBeanDefinitionIfRequired(Element ele, BeanDefinitionHolder bdHolder)方法,进行自定义标签处理。 - 对解析后的
bdHolder进行注册,通过调用BeanDefinitionReaderUtils类的registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)方法。 - 最后发出响应事件,通知相关的监听器,这个bean已经加载完成了。
 
下面就重点对上面四个步骤做个详细介绍。
2 parseBeanDefinitionElement方法分析
| 
 1  | 
 //BeanDefinitionParserDelegate.java @Nullable 		return null;  | 
|---|
上面方法主要分为四个步骤:
- 提取元素中的id以及name属性。
 - 调用
parseBeanDefinitionElement(ele, beanName, containingBean)方法对属性进行解析并封装成AbstractBeanDefinition实例beanDefinition。 - 生成唯一的
beanName,beanName的命名规则为:- 3.1 处 ,如果 
id不为空,则beanName = id。 - 3.2 处,如果 
id为空,但是aliases不空,则beanName为aliases的第一个元素。 - 3.3 处,如果两者都为空,则根据默认规则来设置 
beanName。 
 - 3.1 处 ,如果 
 - 根据所获取的信息(
beanName、aliases、beanDefinition)构造BeanDefinitionHolder实例对象并返回。 
2.1 parseBeanDefinitionElement分析
注意,这个parseBeanDefinitionElement与上一节的parseBeanDefinitionElement不同,这一小节的parseBeanDefinitionElement方法多了一个参数beanName。
| 
 1  | 
 //BeanDefinitionParserDelegate.java @Nullable this.parseState.push(new BeanEntry(beanName)); 		String className = null; 		try { 			// 解析默认 bean 的各种属性 			// 下面的一堆是解析 <bean>……</bean> 内部的子元素 			// 解析元数据 <meta /> 			// 解析构造函数参数 <constructor-arg /> 			bd.setResource(this.readerContext.getResource()); 			return bd; 		return null;  | 
|---|
该方法解析了bean标签的所有属性,有常用的,也有不常用的。
其中有两个重要的方法
createBeanDefinition(className, parent),该方法作用是创建AbstractBeanDefinition对象。AbstractBeanDefinition有什么用,为什么要创建AbstractBeanDefinition对象呢?请看下面的补充分析。parseBeanDefinitionAttributes(ele, beanName, containingBean, bd),该方法作用是解析默认 bean 的各种属性。
2.1.1 创建AbstractBeanDefinition 对象
讲创建AbstractBeanDefinition 对象之前,先来补充下BeanDefinition的一些知识。
org.springframework.beans.factory.config.BeanDefinition ,是一个接口,它描述了一个 Bean 实例的定义,包括属性值、构造方法值和继承它的类的更多信息。
BeanDefinition相关类图
从父关系来看,BeanDefinition 继承 AttributeAccessor 和 BeanMetadataElement 接口。
org.springframework.cor.AttributeAccessor接口,定义了与其它对象的(元数据)进行连接和访问的约定,即对属性的修改,包括获取、设置、删除。org.springframework.beans.BeanMetadataElement接口,Bean 元对象持有的配置元素可以通过getSource()方法来获取。
从子关系来看,BeanDefinition在Spring中有三个实现类。ChildBeanDefinition,RootBeanDefinition,GenericBeanDefinition。
- 三个实现类都继承 
AbstractBeanDefinition抽象类 - 如果配置文件中定义了父 
<bean>和 子<bean>,则父<bean>用RootBeanDefinition表示,子<bean>用ChildBeanDefinition表示。没有父<bean>的就使用RootBeanDefinition表示。 
Spring通过BeanDefinition将配置文件中的配置信息转换为容器的内部表示,并将这些BeanDefiniton注册到BeanDefinitonRegistry中。Spring容器的BeanDefinitionRegistry就像是Spring配置信息的内存数据库,主要是以map的形式保存,后续操作直接从BeanDefinitionRegistry中读取配置信息。
解析属性首先要创建用于承载属性的实例,也就是创建GenericBeanDefinition类型的实例,createBeanDefinition(className, parent)就是实现这个功能。
| 
 1  | 
 //BeanDefinitionParserDelegate.java protected AbstractBeanDefinition createBeanDefinition(@Nullable String className, @Nullable String parentName) 		return BeanDefinitionReaderUtils.createBeanDefinition(  | 
|---|
其中的BeanDefinitionReaderUtils.createBeanDefinition()方法如下:
| 
 1  | 
 // BeanDefinitionReaderUtils.java public static AbstractBeanDefinition createBeanDefinition( 		GenericBeanDefinition bd = new GenericBeanDefinition();  | 
|---|
2.1.2 parseBeanDefinitionAttributes
arseBeanDefinitionAttributes方法是对element所有元素属性进行解析:
| 
 1  | 
 // BeanDefinitionParserDelegate.java public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName,     // 解析 abstract 属性     // 解析 lazy-init 属性     // 解析 autowire 属性     // 解析 depends-on 属性     // 解析 autowire-candidate 属性     // 解析 primary 标签     // 解析 init-method 属性     // 解析 destroy-method 属性     // 解析 factory-method 属性     return bd;  | 
|---|
2.1.3 解析 <bean>...</bean> 内部的子元素
…… 内部的子元素有,meta,lookup-method,replace-method,constructor-arg,property,qualifier。这里我没有做深入研究,小伙伴们想要深入了解可以参考以下博文,讲解的很详细。
3 decorateBeanDefinitionIfRequired方法分析
 decorateBeanDefinitionIfRequired(ele, bdHolder)方法,作用是解析默认标签中的自定义标签元素。
| 
 1  | 
 //BeanDefinitionParserDelegate.java public BeanDefinitionHolder decorateBeanDefinitionIfRequired(Element ele, BeanDefinitionHolder originalDef) { public BeanDefinitionHolder decorateBeanDefinitionIfRequired( BeanDefinitionHolder finalDefinition = originalDef; 		// Decorate based on custom attributes first. 		// Decorate based on custom nested elements.  | 
|---|
这段代码的作用是,寻找自定义标签并根据自定义标签寻找命名空间处理器,并进行进一步的解析。这里没有做具体的分析,感兴趣的小伙伴可以参考其他资料。
4 registerBeanDefinition方法分析
registerBeanDefinition方法的作用:注册BeanDefinition。见如下代码:
| 
 1  | 
 //BeanDefinitionReaderUtils.java public static void registerBeanDefinition( 		// 使用beanName做唯一标识注册 		// 注册所有的别名  | 
|---|
解析的beanDefinition都会被注册到BeanDefinitionRegistry类型的实例registry中,对于beanDefinition的注册分成了两部分:通过beanName注册和通过别名注册。
4.1 通过beanName注册
| 
 1  | 
 // DefaultListableBeanFactory.java /** Whether to allow re-registration of a different definition with the same name. */ /** Map of bean definition objects, keyed by bean name. */ @Override     // 校验 beanName 与 beanDefinition 非空     // 1. 校验 BeanDefinition 。     // 2. 从缓存中获取指定 beanName 的 BeanDefinition         this.frozenBeanDefinitionNames = null;     // 5. 重新设置 beanName 对应的缓存  | 
|---|
这个方法真的很复杂,简要概括逻辑为:
- 对
BeanDefinition进行校验,该校验也是注册过程中的最后一次校验了,主要是对AbstractBeanDefinition的methodOverrides属性进行校验。注意这个与对于XML格式的校验不同。 - 根据 
beanName从缓存中获取BeanDefinition对象。 - 如果缓存中存在,则根据 
allowBeanDefinitionOverriding标志来判断是否允许覆盖。如果允许则直接覆盖。否则,抛出BeanDefinitionStoreException异常。 - 若缓存中没有指定 
beanName的BeanDefinition,则判断当前阶段是否已经开始了 Bean 的创建阶段。如果是,则需要对beanDefinitionMap进行加锁控制并发问题,否则直接设置即可。 - 若缓存中存在该 
beanName或者单例 bean 集合中存在该beanName,则调用#resetBeanDefinition(String beanName)方法,重置BeanDefinition缓存。 
这段代码核心就是this.beanDefinitionMap.put(beanName, beanDefinition)。
4.2 通过别名注册
| 
 1  | 
 // SimpleAliasRegistry.java /** Map from alias to canonical name. */ public void registerAlias(String name, String alias) {  | 
|---|
checkForAliasCircle方法来对别名进行了循环检测。代码如下:
| 
 1  | 
 //SimpleAliasRegistry.java protected void checkForAliasCircle(String name, String alias) {  | 
|---|
5 通知监听器解析及注册完成
通过代码getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder))完成此工作,这里的实现只为扩展,前在Spring中并没有对此事件做任何逻辑处理。当开发人员需要对注册BeanDefinition事件进行监听时可以注册监听器,将处理逻辑写入监听器中。