Spring/SpringBoot系列之Spring源码笔记:解析自定义标签【八】

接上篇文章:Spring/SpringBoot系列之Spring源码笔记:解析默认标签-下【七】

之前分析完了默认标签的解析过程,现在开始分析自定义标签的解析过程。同样先回顾一下,当完成从配置文件到 Document 的转换并提取对应的 root 后,将开始所有元素的解析,而这一过程中便开始了默认标签和自定义标签两种格式的区分,函数如下:
Spring/SpringBoot系列之Spring源码笔记:解析自定义标签【八】_第1张图片
解析自定义标签都是围绕 delegate.parseCustomElement(ele); 这句代码进行的,从上面的函数可以看出,当 Spring 拿到一个元素时首先要做的是根据命名空间进行解析,如果是默认的命名空间,则使用 parseDefaultElement(ele); 方法进行解析,否则使用 parseCustomElement(ele); 方法进行解析。这里先了解一下自定义标签的使用过程。

1. 自定义标签的使用

在很多情况下,需要为系统提供可配置化支持,简单的做法可以直接基于 Spring 的标准 bean 来配置,但配置较为复杂或者需要更多丰富控制的时候,会显得非常笨拙。一般的做法会使用原生态的方式去解析定义好的 XML 文件,然后转化为配置对象。这种方式当然可以解决所有问题,但是实现起来比较繁琐,特别是在配置非常复杂的时候,解析工作是一个不得不考虑的负担。Spring 提供了可扩展 Schema 的支持,这是一个不错的折中方案,扩展 Spring 自定义标签配置大致需要以下几个步骤(前提是要把 Spring 的 Core 包加入项目中)。

  1. 创建一个需要扩展的组件;
  2. 定义一个 XSD 文件描述组件内容;
  3. 创建一个文件,实现 BeanDefinitionParser 接口,用来解析 XSD 文件中的定义和组件定义;
  4. 创建一个 Handler 文件,扩展自 NamespaceHandlerSupport,目的是将组件注册到 Spring 容器;
  5. 编写 Spring.handlers 和 Spring.schemas 文件。

具体过程可以自行百度。这里只说结论:Spring 加载自定义标签的大致流程是遇到自定义标签后就去 Spring.handlers 和 Spring.schemas 中去找对应的 handler 和 XSD,默认位置是 /META-INF/ 下,进而找到对应的 handler 以及解析元素的 Parser,从而完成了整个自定义元素的解析,也就是说自定义标签与默认标签的不同之处在于 Spring 将自定义标签解析的工作委托给了用户去实现。在 Spring 中自定义标签非常有用,例如事务标签:tx(tx:annotation-driven)。

2. 自定义标签解析

接下来探索自定义标签的解析过程:
Spring/SpringBoot系列之Spring源码笔记:解析自定义标签【八】_第2张图片
自定义标签的实现过程的思路其实很简单:

  1. 首先根据对应的 bean 获取对应的命名空间;
  2. 然后根据命名空间解析对应的处理器;
  3. 然后根据用户自定义的处理器进行解析。

2.1 获取标签的命名空间

标签的解析是从命名空间的提取开始的,无论是区分 Spring 中默认标签和自定义标签还是区分自定义标签中不同标签的处理器都是以标签所提供的命名空间为基础的,而至于如何提取对应元素的命名空间其实不需要我们亲自去实现,在 org.w3c.dom.Node 中已经提供了方法供我们直接调用:
Spring/SpringBoot系列之Spring源码笔记:解析自定义标签【八】_第3张图片

2.2 提取自定义标签处理器

有了命名空间,就可以进行 NamespaceHandler 的提取了,继续之前的 parseCustomElement 函数的跟踪,分析 NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri),在 readerContext 初始化的时候其属性 namespaceHandlerResolver 已经被初始化为了 DefaultNamespaceHandlerResolver 的实例,所以,这里调用的 resolve 方法其实调用的是 DefaultNamespaceHandlerResolver 类中的方法。现在进入 DefaultNamespaceHandlerResolver 的 resolve 方法进行查看:
Spring/SpringBoot系列之Spring源码笔记:解析自定义标签【八】_第4张图片
上面的函数清晰地阐述了解析自定义 NamespaceHandler 的过程,前面介绍过,如果要使用自定义标签,那么其中一项必不可少的操作就是在 Spring.handlers 文件中配置命名空间与命名空间处理器的映射关系。只有这样,Spring 才能根据映射关系找到匹配的处理器,而寻找匹配的处理器就是在上面函数中实现的。当获取到自定义的 NamespaceHandler 之后就可以进行处理器初始化并解析了。

当得到自定义命名空间处理后会马上执行 namespaceHandler.init() 来进行自定义 BeanDefinitionParser 的注册。在这里,你可以注册多个标签解析器。

注册后,命名空间处理器就可以根据标签的不同来调用不同的解析器进行解析。那么,基本上可以推断 getHandlerMappings 的主要功能就是读取 Spring.handlers 配置文件并将配置文件缓存在 map 中:
Spring/SpringBoot系列之Spring源码笔记:解析自定义标签【八】_第5张图片
这里借助了工具类 PropertiesLoaderUtils 对属性 handlerMappingsLocation 进行了配置文件的读取。

2.3 标签解析

得到了解析器以及要分析的元素后,Spring 就可以将解析工作委托给自定义解析器去解析了。在 Spring 中的代码为:
Spring/SpringBoot系列之Spring源码笔记:解析自定义标签【八】_第6张图片
解析过程中首先是寻找元素对应的解析器,进而调用解析器中的 parse 方法:
Spring/SpringBoot系列之Spring源码笔记:解析自定义标签【八】_第7张图片
Spring/SpringBoot系列之Spring源码笔记:解析自定义标签【八】_第8张图片
在 parseInternal 中并不是直接调用自定义的 doParse 函数,而是进行了一系列的数据准备,包括对 beanClass、scope、lazyInit 等属性的准备。
Spring/SpringBoot系列之Spring源码笔记:解析自定义标签【八】_第9张图片
到此为止已经完成了 Spring 中全部的解析工作,也就是说已经理解 Spring 将 bean 从配置文件到加载到内存中的全过程,而接下来的任务便是如何使用这些 bean,后面的文章会介绍 bean 的加载。

你可能感兴趣的:(java,java,spring)