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