Spring xml默认标签的解析

                    默认标签的解析

之前提高过Spring中的标签包括默认标签和自定义标签两种,而两种标签的用法以及解析方式存在着很大的不同,本章节重点带领读者详细分析默认标签的解析过程。
默认标签的解析是在parseDefaultElement函数中进行的,函数中的功能逻辑一目了然,分别对4种不同标签(import、alias、bean、和beans)做了不同的处理。

Spring xml默认标签的解析_第1张图片

bean标签的解析及注册

在4种标签的解析中,对bean标签的解析最为复杂也是最重要,所以我们从此标签开始深入分析,如果能理解此标签的解析过程,其他标签的解析自然会迎刃而解。首先我们进入函数processBeanDefinition(ele, delegate)。

Spring xml默认标签的解析_第2张图片

上述逻辑如下:
(1)首先委托BeanDefinitionDelegate类的parseBeanDefinitionElement方法进行元素解析,返回BeanDefinitionHolder类型的实例bdHolder,经过这个方法后,bdHolder实例已经包含我们配置文件中配置的各种属性了,例如class、name、id、alias之类的属性。
(2)当返回的bdHolder不为空的情况下若存在默认标签的子节点下再有自定义属性,还需要再次对自定义标签进行解析。
(3)解析完成后,需要对解析后的bdHolder进行注册,同样,注册操作委托给了BeanDefinitionReaderUtils的registerBeanDefinition方法。是一个一个bean进行注册的。
(4)最后发出响应事件,通知相关的监听器,这个Bean已经加载完成了。

解析BeanDefinition

下面我们就针对各个操作做具体分析。首先我们从元素解析及信息提取开始,也就是BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele),进入BeanDefinitionDelegate类的parseBeanDefinitionElement方法。

Spring xml默认标签的解析_第3张图片
Spring xml默认标签的解析_第4张图片

以上两个代码块截图是一个方法,是对默认标签解析的全过程了。对xml解析如洋葱般一层一层剥。整个解析过程我们很明了了。在开始对属性展开全面解析钱,Spring在外层又做了一个当前层的功能架构,在当前层完成的主要工作包括如下内容。
(1)提取元素中的id以及name属性。
(2)进一步解析其他所有属性并统一封装至GenericBeanDefinition类型的实例中。
(3)如果检测到bean没有指定beanName,那么使用默认规则为此bean生成beanName。
(4)将获取到的信息封装到BeanDefinitionHolder的实例中。
进一步查看(2)中对标签其他属性解析的过程。

Spring xml默认标签的解析_第5张图片
Spring xml默认标签的解析_第6张图片

终于,bean标签的所有属性,不论常用的还是不常用的我们都看到了,尽管有些复杂的属性还需要进一步的解析,不过丝毫不会影响我们兴奋的心情。接下来,我们继续一些复杂标签属性的解析。

创建用于属性承载的BeanDefinition

BeanDefinition是一个接口,在Spring中存在三种实现:RootBeanDefinition、ChildBeanDefinition以及GenericBeanDefinition。三种实现均继承了AbstractBeanDefinition,其中BeanDefinition是配置文件元素标签在容器中的内部表示形式。元素标签拥有class、scope、lazy-init等配置属性,BeanDefinition则提供了相应的beanClass、scope、lazyInit属性,BeanDefinition和中的属性是一一对应的。其中RootBeanDefinition是最常用的实现类,它对应一般性的元素标签,GenericBeanDefinition是自2.5版本以后新加入的bean文件配置属性定义类,是一站式服务类。
在配置文件中可以定义父和子,父用RootBeanDefinition表示,而子用ChildBeanDefinition表示,而没有父就使用RootBeanDefinition表示。AbstractBeanDefinition对两者共同的类信息进行抽象。
Spring通过BeanDefinition将配置文件中的配置信息转换为容器的内部表示,并将这些BeanDefinition注册到BeanDefinitionRegistry中。Spring容器的BeanDefinitionRegistry就像是Spring配置信息的内存数据库,主要是以Map的形式保存,后续操作直接从BeanDefinitionRegistry中读取配置信息。查看BeanDefinition继承图:

Spring xml默认标签的解析_第7张图片

由此可知,要解析属性首先要创建用于承载属性的实例,也就是创建GenericBeanDefinition类型的实例。而代码createBeanDefinition(className, parent)的作用就是实现此功能。

Spring xml默认标签的解析_第8张图片
Spring xml默认标签的解析_第9张图片

解析各种属性

当我们创建了bean信息的承载实例后,便可以进行bean信息的各种属性解析了,首先我们进入parseBeanDefinitionAttributes方法。parseBeanDefinitionAttributes方法是对element所有元素属性进行解析:

Spring xml默认标签的解析_第10张图片
Spring xml默认标签的解析_第11张图片
Spring xml默认标签的解析_第12张图片

我们可以清楚地看到Spring完成了对所有bean属性的解析,这些属性中有很多是我们经常使用的,同时我相信也一定会有或多或少的属性是读者不熟悉或者是没有使用过的。

解析子元素meta

在开始解析元数据的分析前,我们先回顾下元数据meta属性的使用。

       

这段代码并不会现在MyTestBean的属性当中,而是一个额外的声明,当需要使用里面的信息的时候可以通过BeanDefinition的getAttribute(key)方法进行获取。
对meta属性的解析代码如下:

Spring xml默认标签的解析_第13张图片

。。。。。。对属性的解析源码后续再添加



AbstractBeanDefinition属性

至此我们便完成了对xml文档到GenericBeanDefinition的转换,也就是说到这里,XML中所有的配置都可以在GenericBeanDefinition的实例类中找到对应的配置。
GenericBeanDefinition只是子类实现,而大部分的通用属性都保存在了AbstractBeanDefinition中,那么我们再次通过AbstractBeanDefinition的属性来回顾一下我们都解析了那些对应的配置。


Spring xml默认标签的解析_第14张图片
Spring xml默认标签的解析_第15张图片
Spring xml默认标签的解析_第16张图片
Spring xml默认标签的解析_第17张图片
Spring xml默认标签的解析_第18张图片

解析默认标签中的自定义标签元素

完成了默认标签的解析与提取过程,或许涉及的内容太多,我们已经忘了我们从哪个函数开始了,回到默认标签解析函数的起始函数:

Spring xml默认标签的解析_第19张图片

我们已经用了大量的篇幅分析了BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele)这句代码,接下来,我们要进行 bdHolder = delegate.decorateBeanDefinitionRequired(ele, bdHolder)代码的分析,首先大致了解下这句代码的作用,其实我们可以从语义上分析:如果需要的话就对beanDefinition进行装饰,这句代码到底是什么功能呢?代码适用于这样的场景:

   

当Spring中的bean使用的是默认的标签配置,但是其中的子元素却使用了自定义的配置时,这句代码便会起作用了。可能有人会疑问,对bean的解析分为两种类型,一种是默认类型的解析,另一种是自定义类型的解析,这不正是自定义类型的解析吗?为什么会在默认类型解析中单独添加一个方法处理呢?确实,这个问题很让人迷惑,但是,不知道你是否发现,这个自定义类型并不是以Bean的形式出现的呢?我们之前讲过的两种类型的不同处理只是针对Bean的,这里我们看到,这个自定义类型其实是属性。继续分析一下这段代码:   
看如下代码,函数方法中第三个参数设置为空,第三个参数是父类bean,当对某个嵌套配置进行分析时,这里需要传递父类beanDefinition。分析源码得知这里传递的参数其实是为了使用父类的scope属性,以备子类若没有设置scope时默认使用父类的属性,这里分析的是顶层配置,所以传递null。将第三个参数设置为空进一步跟踪函数:

Spring xml默认标签的解析_第20张图片

上面的代码,我们看到函数分别对元素的所有属性以及子节点进行了decorateIfRequired函数的调用,我们继续跟踪代码:

Spring xml默认标签的解析_第21张图片

程序走到这里,条理其实已经非常清楚了,首先获取属性或者元素的命名空间,以此来判断该元素或者属性是否适用于自定义标签的解析条件,找出自定义类型所对应的NamespaceHandler并进行进一步解析。在自定义标签解析的章节我们会重点讲解,这里暂时先略过。
我们总结下decorateBeanDefinitionIfRequired方法的作用,在decorateBeanDefinitionIfRequired中我们可以看到对于程序默认的标签的处理其实是直接略过的,因为默认的标签到这里已经被处理完了,这里只对自定义的标签或者说对bean的自定义属性感兴趣。在方法中实现了寻找自定义标签并根据自定义标签寻找命名空间处理器,并进行进一步的解析。

注册解析的BeanDefinition

对于配置文件,解析也解析完了,装饰也装饰完了,对于得到的BeanDefinition已经可以满足后续的使用需求了,唯一还剩下的工作就是注册了,也就是processBeanDefinition函数中的BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegister()代码)

Spring xml默认标签的解析_第22张图片

从上面的代码可以看出,解析的beanDefinition都会被注册到BeanDefinitionRegister类型的实例register中,而对于beanDefinition的注册分成了两部分:通过beanName的注册以及通过别名的注册。

1.通过beanName注册BeanDefinition

对于BeanDefinition的注册,或许很多人认为的方式就是将beanDefinition直接放入map中就好了,使用beanName作为key。确实,Spring就是这么做的,只不过除此之外,它还做了点别的事情。


Spring xml默认标签的解析_第23张图片
Spring xml默认标签的解析_第24张图片
Spring xml默认标签的解析_第25张图片

上面的代码中我们看到,在对于bean的注册处理方式上,主要进行了几个步骤。
(1)对AbstractBeanDefinition的校验。在解析XML文件的时候我们提过校验,但是此校验非彼校验,之前的校验是针对于XML格式的校验,而此时的校验是针对于与AbstractBeanDefinition的methodOverrides属性的。
(2)对beanName已经注册的情况的处理。如果设置了不允许bean的覆盖,则需要抛出异常,否则直接覆盖。
(3)加入map缓存。
(4)清除解析之前留下的对应beanName的缓存。

2.通过别名注册BeanDefinition

别名注册相对于名称注册Bean更容易读懂。

Spring xml默认标签的解析_第26张图片

由以上代码中可以得到注册alias的步骤如下:
(1)alias与beanName相同情况处理。若alias与beanName并名称相同则不需要处理并删除掉原有alias。
(2)alias覆盖处理。若aliasName已经使用并没有指向了另一个beanName则需要用户的设置进行处理。
(3)alias循环检查。当A->B存在时,若再次出现A->C->B时候则会抛出异常。
(4)注册alias。

通知监听器解析并注册完成

通过代码getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder))完成此工作,这里的实现只为扩展,当程序开发人员需要对注册BeanDefinition事件进行监听时可以通过注册监听器的方式并将处理逻辑写入监听器中,目前Spring中并没有对此事件做任何逻辑处理。

你可能感兴趣的:(Spring xml默认标签的解析)