该系列文章是本人在学习 Spring 的过程中总结下来的,里面涉及到相关源码,可能对读者不太友好,请结合我的源码注释 Spring 源码分析 GitHub 地址 进行阅读
Spring 版本:5.1.14.RELEASE
开始阅读这一系列文章之前,建议先查看《深入了解 Spring IoC(面试题)》这一篇文章
该系列其他文章请查看:《死磕 Spring 之 IoC 篇 - 文章导读》
解析自定义标签(XML 文件)上一篇《BeanDefinition 的解析阶段(XML 文件)》文章分析了 Spring 处理 org.w3c.dom.Document 对象(XML Document)的过程,会解析里面的元素。默认命名空间(为空或者 )的元素,例如 <bean /> 标签会被解析成 GenericBeanDefinition 对象并注册。本文会分析 Spring 是如何处理非默认命名空间的元素,通过 Spring 的实现方式我们如何自定义元素
先来了解一下 XML 文件中的命名空间:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd https://www.springframework.org/schema/context/spring-context.xsd"> <context:component-scan base-package="org.geekbang.thinking.in.spring.ioc.overview" /> <bean> <property value="1"/> <property value="小马哥"/> </bean> </beans>上述 XML 文件 <beans /> 的默认命名空间为 ,内部的 <bean /> 标签没有定义命名空间,则使用默认命名空间
<beans /> 还定义了 context 命名空间为 ,那么内部的 <context:component-scan /> 标签就不是默认命名空间,处理方式也不同。其实 Spring 内部自定义了很多的命名空间,用于处理不同的场景,原理都一样,接下来会进行分析。
自定义标签的实现步骤扩展 Spring XML 元素的步骤如下:
编写 XML Schema 文件(XSD 文件):定义 XML 结构
自定义 NamespaceHandler 实现:定义命名空间的处理器,实现 NamespaceHandler 接口,我们通常继承 NamespaceHandlerSupport 抽象类,Spring 提供了通用实现,只需要实现其 init() 方法即可
自定义 BeanDefinitionParser 实现:绑定命名空间下不同的 XML 元素与其对应的解析器,因为一个命名空间下可以有很多个标签,对于不同的标签需要不同的 BeanDefinitionParser 解析器,在上面的 init() 方法中进行绑定
注册 XML 扩展(META-INF/spring.handlers 文件):命名空间与命名空间处理器的映射
编写 Spring Schema 资源映射文件(META-INF/spring.schemas 文件):XML Schema 文件通常定义为网络的形式,在无网的情况下无法访问,所以一般在本地的也有一个 XSD 文件,可通过编写 spring.schemas 文件,将网络形式的 XSD 文件与本地的 XSD 文件进行映射,这样会优先从本地获取对应的 XSD 文件
Spring 内部自定义标签预览在 spring-context 模块的 ClassPath 下可以看到有 META-INF/spring.handlers、META-INF/spring.schemas 以及对应的 XSD 文件,如下:
META-INF/spring.handlers
http\://www.springframework.org/schema/context=org.springframework.context.config.ContextNamespaceHandler http\://www.springframework.org/schema/jee=org.springframework.ejb.config.JeeNamespaceHandler http\://www.springframework.org/schema/lang=org.springframework.scripting.config.LangNamespaceHandler http\://www.springframework.org/schema/task=org.springframework.scheduling.config.TaskNamespaceHandler http\://www.springframework.org/schema/cache=org.springframework.cache.config.CacheNamespaceHandler
META-INF/spring.schemas
http\://www.springframework.org/schema/context/spring-context.xsd=org/springframework/context/config/spring-context.xsd http\://www.springframework.org/schema/jee/spring-jee.xsd=org/springframework/ejb/config/spring-jee.xsd http\://www.springframework.org/schema/lang/spring-lang.xsd=org/springframework/scripting/config/spring-lang.xsd http\://www.springframework.org/schema/task/spring-task.xsd=org/springframework/scheduling/config/spring-task.xsd http\://www.springframework.org/schema/cache/spring-cache.xsd=org/springframework/cache/config/spring-cache.xsd https\://www.springframework.org/schema/context/spring-context.xsd=org/springframework/context/config/spring-context.xsd https\://www.springframework.org/schema/jee/spring-jee.xsd=org/springframework/ejb/config/spring-jee.xsd https\://www.springframework.org/schema/lang/spring-lang.xsd=org/springframework/scripting/config/spring-lang.xsd https\://www.springframework.org/schema/task/spring-task.xsd=org/springframework/scheduling/config/spring-task.xsd https\://www.springframework.org/schema/cache/spring-cache.xsd=org/springframework/cache/config/spring-cache.xsd ### ... 省略