Spring 系列之 Spring 源码笔记:解析默认标签-下【七】

接上篇文章:Spring 系列之 Spring 源码笔记:解析默认标签-中【六】

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

到这里已经完成了分析默认标签的解析与提取过程,因为涉及的内容太多,可以已经忘记了从哪个函数开始的,再次回顾下默认标签解析函数的起始函数:
在这里插入图片描述
前面已经用了大量的篇幅分析 BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); 这行代码了,下面要进行 bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); 的分析,首先可以从语义上进行分析:如果需要的话就对 beanDefinition 进行装饰,这句代码适用于这样的场景:

<bean id="test" class="test.MyClass">
	<mybean:user username="aaa"/>
</bean>

当 Spring 中的 bean 使用的是默认的标签配置,但是其中的子元素却使用了自定义的配置时,这句代码便会起作用了。但是要注意,虽然这个地方是自定义类型的解析,但是为什么会在默认类型解析中单独添加一个方法进行处理?其实这个自定义类型并不是以 Bean 的形式出现的,之前总结过两种类型的不同处理只是针对 Bean 的,这里这个自定义类型其实是属性。接下来继续分析这段代码的逻辑:
在这里插入图片描述
在这里插入图片描述
上面的函数中分别对元素的所有属性以及子节点进行了 decorateIfRequired 函数的调用,继续跟踪代码:
在这里插入图片描述
到这里,条理已经非常清楚了,首先获取属性或者元素的命名空间,以此来判断该元素或者属性是否适用于自定义标签 (后续的文章会进行总结) 的解析条件,找出自定义类型所对应的 NamespaceHandler 并进行进一步解析。

总结下 decorateBeanDefinitionIfRequired 方法的作用,在该方法中可以看到程序默认的标签的处理其实是直接略过的,因为默认的标签到这里已经被处理完了,这里只对自定义的标签或者对 bean 的自定义属性感兴趣。在方法中实现了寻找自定义标签并根据自定义标签寻找命名空间处理器,并进行进一步的解析。

1.4 注册解析的 BeanDefinition

对于配置文件,解析和装饰工作已经完成了,对于得到的 beanDefinition 已经可以满足后续的使用要求了,剩下的唯一工作就是注册了,也就是 processBeanDefinition 函数中的 BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry()),开始跟踪代码:
在这里插入图片描述
从上面的代码可以看出,解析的 beanDefinition 都会被注册到 BeanDefinitionRegistry 类型的实例 registry 中,而对于 beanDefinition 的注册分成了两部分:通过 beanName 的注册以及通过别名的注册。

1.4.1 通过 beanName 注册 BeanDefinition

对于 beanDefinition 的注册,或许很多人认为的方式就是将 beanDefinition 直接放入 map 中,使用 beanName 作为 key。确实 Spring 就是这么做的,只不过它还做了些别的事情:
在这里插入图片描述
实现类:DefaultListableBeanFactory。

从上面的代码可以看出,在对于 bean 的注册处理方式上,主要进行了几个步骤:

  1. 对 AbstractBeanDefinition 的校验。在解析 XML 文件的时候提到过校验,但是这里的校验是针对于 AbstractBeanDefinition 的 methodOverrides 属性的。
  2. 对 beanName 已经注册的情况的处理,如果设置了不允许 bean 的覆盖,则需要抛出异常,否则直接覆盖。
  3. 加入 map 缓存。
  4. 清除解析之前留下的对应 beanName 的缓存。

1.4.2 通过别名注册 BeanDefinition

理解了注册 bean 的原理后,理解注册别名的原理就简单多了。
在这里插入图片描述
实现类:SimpleAliasRegistry

注册 alias 的步骤如下:

  1. alias 与 beanName 相同情况处理。若 alias 与 beanName 名称相同则不需要处理并删除掉原有 alias。
  2. alias 覆盖处理。若 aliasname 已经使用并已经指向了 beanName 则结束方法。
  3. alias 循环检查。当 A->B存在时,若再次出现 A->C->B 则会抛出异常。
  4. 注册 alias。

1.5 通知监听器解析及注册完成

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

2. alias 标签的解析

之前提到过,对配置文件的解析包括对 import 标签、alias 标签、bean 标签、beans 标签的处理,现在我们已经完成了最重要也是最核心的功能,其他的解析步骤都是围绕 bean 标签的解析进行的。下面开始总结 alias 标签的解析。

在对 bean 进行定义时,除了使用 id 属性来指定名称之外,为了提供多个名称,可以使用 alias 标签来指定。而所有的这些名称都指向同一个 bean,在某些情况下提供别名非常有用,比如为了让应用的每一个组件能更容易地对公共组件进行引用。

然而,在定义 bean 时就指定所有的别名并不总是恰当的。有时我们期望能在当前位置为那些在别处定义的 bean 引入别名。在 XML 配置文件中,可用单独的 alias 元素来完成 bean 别名的定义。如配置文件中定义了一个 JavaBean:

<bean id="testBean" class="com.test"/>

要给这个 JavaBean 增加别名,以便不同对象来调用。我们就可以直接使用 bean 标签中的 name 属性:

<bean id="testBean" name="testBean,testBean2" class="com.test"/>

同样,Spring 还有另外一种声明别名的方式:

<bean id="testBean" class="com.test"/>
<alias name="testBean" alias="testBean,testBean2"/>

源码中方法上注释的很详细,这样一来,每个组件及主程序就可以通过唯一名字来引用同一个数据源而互不干扰。

在之前的已经总结了对于 bean 中 name 元素的解析,下面看下 alias 标签的解析过程:
在这里插入图片描述
可以看到,跟之前的 bean 中的 alias 解析大同小异,都是将别名与 beanName 组成一对注册至 registry 中。

3. import 标签的解析

对于 Spring 配置文件的编写,经历过庞大项目的人想起来都会瑟瑟发抖,配置文件太多了,而且也很繁琐。不过,分模块是大多数人能想到的方法,但是,怎么分模块,就仁者见仁智者见智了。使用 import 是个好办法,例如我们可以构造这样的 Spring 配置文件:

applicationContext.xml

<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "https://www.springframework.org/dtd/spring-beans-2.0.dtd">
<beans>
	<import resource="customerContext.xml"/>
	<import resource="systemContext.xml"/>
	... ...
<beans>

applicationContext.xml 文件中使用 import 的方式导入有模块配置文件,以后若有新模块加入,那就可以简单修改这个文件了。这样大大简化了配置后期维护的复杂度,并使配置模块化,易于管理。下面看下 Spring 是如何解析 import 配置文件的:
在这里插入图片描述
总结下上面的代码,在解析 import 标签时,Spring 进行解析的步骤大致如下:

  1. 获取 resource 属性所表示的路径。
  2. 解析路径中的系统属性,格式如“${user.dir}”
  3. 判定 location 是绝对路径还是相对路径
  4. 如果是绝对路径,则递归调用 bean 的解析过程,进行另一次解析
  5. 如果是相对路径则计算出绝对路径并进行解析
  6. 通知监听器,解析完成

4. 嵌入式 beans 标签的解析

对于嵌入式 beans 标签,非常类似于 import 标签所提供的功能,使用如下:
在这里插入图片描述
对于嵌入式 beans 标签来讲,与单独的配置文件并没有太大的差别,无非是递归调用 beans 的解析过程。所以这里不再进行赘述。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值