XML文件默认标签的解析
之前提到Spring的配置文件中有两类标签,一类是自定义的标签,另一类就是Spring的默认表签。默认标签的解析实在类DefaultBeanDefinitionDocumentReader的parseDefaultElement方法中解析。分别对import(importBeanDefinitionResource)、alias(processAliasRegistration)、bean(processBeanDefinition)和beans(doRegisterBeanDefinitions)四类标签做了不同的处理。
Bean标签的解析及注册
进入DefaultBeanDefinitionDocumentReader.processBeanDefinition方法。它的大致处理流程如下:
- 先委托BeanDefinitionParserDelegate类的parseBeanDefinitionElement方法进行元素解析。返回BeanDefinitionHolder类型的实例bdHolder,进过这个方法之后,bdHolder实例已经包含了我们配置文件中的各种属性了,例如:class、name、id、alias之类的属性。
- 在bdHolder不为空的情况下,如果还有自定的标签,那么还要解析自定义的标签。
- 解析完成后,需要对解析后的bdHolder进行注册,同样,注册操作委托了BeanDefinitionReaderUtils的registerBeanDefinition方法。
- 最后发出响应事件,通知相关的监听器,这个bean已经加载完成了。
解析BeanDefinition
进入BeanDefinitionParserDelegate.parseBeanDefinitionElement方法。执行的过程:
- 先解析获取id和name属性。
- 从文档我们发现执行了一系列的bean检查。检查是否重名等。然后获取其他属性,创建GenericBeanDefinition的实例。
- 如果bean没有指定beanName,那么使用默认规则为此bean生成beanName。
- 将获取到的信息封装到BeanDefinitionHolder的实例中。
创建用于属性承载的BeanDefinition
BeanDefinition(配置bean在容器中表现形式)在Spring中有三个实现类:GenericBeanDefinition(2.5才加入)、RootBeanDefinition(用于父类的bean表示,没父类的bean也用它)和ChildBeanDefinition(子bean的表示)。三种实现都是继承AbstractBeanDefinition。
Spring要将BeanDefinition注册到BeanDefinitionRegistry中。BeanDefinitionRegistry就像是Spring的内存数据库,主要是以map的形式保存。
解析各种属性
这个去看源码,里面会设计到解析bean标签的各种属性。
解析子元素meta
这个看看源码的实现即可
解析子元素lookup-method
关于这个部分的知识,首先来介绍什么是lookup-method。可以查看Spring的方法注入lookup-method。大概的意思就是定义了一个抽象类,然后创建了一个bean!但是抽象类并没有实现怎么实例化呢?原因就在于这个lookup-method!这个抽象方法在被spring使用cglib给实现了,方式就是返回另一个符合条件的bean。这样做的好处就是解耦,不需要再写一个实现类来实现你的功能。实现起来就是做配置,方便简单。
解析的过程也简单,取得bean的所有子标签,找到所有lookup-method标签,建立LookupOverride类的实例,并放入AbstractBeanDefinition的methodOverrides数组属性中。
解析子元素replaced-method
首先来介绍一下这个类的使用,这个和lookup-method有类似的功能。首先定义一个父类的bean,然后定义一个子类bean,子类要覆盖父类中的一个方法。在父类中的bean中引用子类的bean,这样调用这个bean时,使用的就是子类的方法。
解析的过程和上面一样,创建ReplaceOverride,最后也是放入MethodOverrides中。
解析constructor-arg属性
读了源码之后你会发现有三种定义方式index、type、name。三种类型都会创建ConstructorArgumentValues.ValueHolder的实例来存储。index的会被放入addIndexedArgumentValue(index, valueHolder)。type和name将放入addGenericArgumentValue(valueHolder)。
对该标签属性的解析是在parsePropertyValue方法中。首先它不处理description和meta元素,同时验证ref和value属性以及子元素的状态,出错就报。然后对ref和vaule属性进行处理,分别使用RuntimeBeanReference和TypedStringValue进行封装,返回!最后讲一下对子元素的处理。parsePropertySubElement(subElement, bd); 例如这样的:
<constructor-arg>
<map>
<entry key="key" value="value">
</entry>
</map>
</constructor-arg>
这里就是调用通用的解析方式进行解析。返回的是Object类,所以解析的结果是不定的。也就是说bean中的子元素,在这个标签下也可以用,并解析!
解析子元素Property
使用PropertyValue封装,放入BeanDefinition的PropertyValue列表中。
解析qualifier
我们可以通过qualifier指定spring注入的名称。
<bean id="helloBean" class="com.gavin.bean.HelloBean">
<property name="greet" value="GavinZhang"></property>
<qualifier type="com.gavin.bean.HelloBean" value="hb"/>
</bean>
AbstractBeanDefinition属性
上一个小节,介绍了XML到GenericBeanDefinition的解析过程。GenericBeanDefinition是继承AbstractBeanDefinition属性的。
解析自定义的标签
从DefaultBeanDefinitionDocumentReader.processBeanDefinition函数继续开始吧!我们已经介绍了delegate.parseBeanDefinitionElement(ele);接下介绍delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);假如我们有如下的bean:
<bean id="test" class="test.MyClass">
<mybean:user username="aaa"/>
</bean>
我们来看一下,decorateBeanDefinitionIfRequired方法。传入bean标签和标签的holder,父Bean标签(这里为null,是为了设置子Bean标签的默认值)。找到属性和子标签后调用decorateIfRequired方法进行处理。他会通过标签的命名空间找到对应处理的Handler。这里先这样!以后再介绍!
注册解析的BeanDefinition
我们继续看DefaultBeanDefinitionDocumentReader.processBeanDefinition,下面到了注册Bean的代码了。
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
进入代码阅读,发现首先要验证AbstractBeanDefinition的methodOverrides属性。然后开始注册Bean,先找注册Bean中是否有存在的,有的话查询是否可被覆盖,不能覆盖报错,能覆盖的话就进行覆盖(更新beanDefinitionMap就可以了)。没找到就注册新的Bean。beanDefinitionMap插入一条,beanDefinitionNames加入相应的Name,manualSingletonNames加入一条,用于单例模式。
通知监听器解析及注册完成
通过代码getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));完成此工作,这里的实现只为扩展,当程序开发人员需要对注册BeanDefinition事件进行监听的时候可以通过注册监听的方式将处理逻辑写入监听器中,目前Spring中并没有对此事件做任何处理逻辑。
Alias标签的解析
关于这部分的知识不做深入学习,alias可以使用Bean中的alias属性定义,也可以使用alias标签进行定义,这里显然是使用了alias标签定义的解析。
回到,DefaultBeanDefinitionDocumentReader.parseDefaultElement方法,进入processAliasRegistration方法。
Import标签的解析
- 获取Resource路径
- 解析路径中的系统属性格式
- 判断是相对还是绝对地址
- 如果是绝对地址,那么递归调用Bean的解析过程,进行另一次的解析。
- 如果是相对路径,则计算出绝对路径进行4
- 通知监听器解析完成
嵌入式Beans解析
只是简单递归调用Bean的解析过程,不用多说!