前言
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事件进行监听时可以注册监听器,将处理逻辑写入监听器中。