Spring源码之bean标签的解析与注册
前言
在上一篇中,最后讲到了parseDefaultElement方法和parseCustomElement方法,对标签进行解析。先讲一下对bean标签的解析。
1 | protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) { |
- 首先委托BeanDefinitionDelegate类的parseBeanDefinitionElement方法进行元素解析,返回BeanDefinitionHolder类型的实例bdHolder,此时,bdHolder就已经包含配置文件中的配置的各种属性了,例如:class、name、id
- 当返回的bdHolder不为空时若存在默认标签的子节点下再有自定义属性,还需要对自定义标签进行解析。
- 解析完成后,需要对解析后的bdHolder进行注册,同样,注册操作委托给了BeanDefinitionReaderUtils的registerDefinition方法。
- 最后发出响应事件,通知相关的监听器,这个bean已经加载完成了。
下面依次解释每个步骤的含义。
解析BeanDefinition
先解释一个类BeanDefinitionHolder,
1 | public class BeanDefinitionHolder implements BeanMetadataElement { |
从上面可以看出,BeanDefinition是BeanDefinition的一个持有者,并存储bean的姓名和别名。
1 |
|
主要步骤:
- 提取元素中的id以及name属性
- 进一步解析其他所有属性并统一封装至GenericBeanDefinition类型中的实例中。
- 如果监测到bean没有指定的beanName,那儿使用默认规则为此Bean生成beanName
- 将获取到的信息封装到BeanDefinitionHolder的实例中。
先简单介绍BeanDefinition这个接口。
1 | /** |
上面是源码文档解释,从上可以看出,它描述了一个bean实例,有属性值和构造函数参数值,具体的信息由具体的子类来实现。
在配置文件中<bean>元素拥有class、scope、lazy-init等配置属性,BeanDefinition则提供了相应的beanClass、Scope、lazyInit属性,BeanDefinition和<bean>中的元素一一对应。
具体的类RootBeanDefinition、GenericBeanDefinition和ChildBeanDefinition。
其中RootBeanDefinition是最常用的类,在配置文件中可以配置父<bean>和子<bean>,子<bean>用childBeanDefinition表示,没有子类时,直接用RootBeanDefinition表示。
Spring通过BeanDefinition将配置文件中的<bean>配置信息转化为容器的内部表示,并将这些BeanDefinition注册到BeanDefinitionRegistry中。Spring容器中的BeanDefinitionRegistry就像是Spring配置信息的内存数据库,主要是以map的形式保存,后续操作直接从BeanDefinitionRegistry中读取配置信息。
BeanDefinitionParserDelegate
1 | * Stateful delegate class used to parse XML bean definitions. |
从上面的定义中可以看出它是代表类,用于处理XML Bean定义的类,干的都是脏活累活。
import/alias/bean等element以及element的子节点以及属性都是它解析并且填充到BeanDefinition中然后使用ReaderContext中的Registry(实际就是DefaultListableBeanFactory)来将该BeanDefinition注册。
Bean的注册
Spring提供了BeanFactory对Bean进行获取,但Bean的注册和管理并不是在BeanFactory中进行,而是在BeanDefinitionRegistry中进行,这里BeanFactory只提供了查阅的功能。
Spring的Bean信息注册保存在一个个BeanDefinition中的。
我们以ClassPathXmlApplicationContext为例
- 在它的构造函数中主要的逻辑方法有两个。
首先调用setConfigLocations()来设置配置文件路径并可以修改配置文件中的属性值。
然后调用refresh()
方法。它是代码的核心,用来对Bean进行注册和初始化。 - 在refresh()方法中,主要有下面几个步骤
- BeanFactory的初始化,并且加载配置文件中相关的bean信息。
- 调用postProcessBeanFactory(beanFactory)抽象方法,用于供给子类对已经生成的BeanFactory的一些信息进行定制,registerBeanPostProcessors对BeanPostProcessor进行注册。
BeanPostProcessor是一个扩展点,有两个方法,分别对应IOC容器对对象初始化前的操作和初始化后的操作。 - 初始化国际化信息
- 注册和调用相关的监听器
- 实例化注册的bean信息
- 对于bean的注册,我们需要关注refreshBeanFactory()方法,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
protected final void refreshBeanFactory() throws BeansException {
if (hasBeanFactory()) { // 如果BeanFactory已经创建则对其进行销毁
destroyBeans();
closeBeanFactory();
}
try {
// 创建BeanFactory实例
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId()); // 为当前BeanFactory设置一个标识id
customizeBeanFactory(beanFactory); // 设置BeanFacotry的定制化属性信息
loadBeanDefinitions(beanFactory); // 加载xml文件信息
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
从中可以看出中间最重要的方法是loadBeanDefinitions
将加载XML文件中的bean信息交给XmlBeanDefinitionReader来处理。
它会依次读取每个xml配置文件中的bean信息,
将xml文件转化为一个InputStream,再转化为InputSource,进而转化为一个Document对象,该对象保存着各个XML文件中各个节点和子节点的相关信息,然后获取到Document的根节点信息,调用BeanDefinitionDocumentReader.registerBeanDefinitions(root)
进行注册。
然后开始解析xml文件,封装成BeanDefinition并完成注册,该点在文章开头已经讲解。