《系列一》-- 5、xml配置文件解析之[自定义]命名空间[标签]的解析
阅读之前要注意的东西:本文就是主打流水账式的源码阅读,主导的是一个参考,主要内容需要看官自己去源码中验证。全系列文章基于 spring 源码 5.x 版本。
引子
系列1 - bean 标签解析:
4、xml配置文件解析之【默认】命名空间【标签】的解析.md
5、xml配置文件解析之【自定义】命名空间【标签】的解析.md
系列2 - bean 获取: getBean() 做了什么
前言
一句话概括:
- 如何基于spring 的xml配置文件整活:怎么创建我们自己的标签?
- 自定义标签读取,并注册到:BeanFactory 中 (BeanDefinitionRegistry)
【只读取配置内容,并注册管理】
1 为什么要用自定义命名空间标签 ?
因为spring-beans 支持的只是将默认配置读取,并解析到 Bean 这种比较通用的逻辑。
如果我们希望对 xml 注入的bean的 成员变量 进行:
- 二次加工
- 参数值校验
等等个性化操作时,spring 官方实现的标签就不太适合做这种操作了,所以我们需要根据自己的业务去自定义标签。
2 前文回顾
书接上回,上文讲了 spring 默认命名空间标签解析:

本文将从 自定义标签的解析开始展开。
稍微回顾下,这里的:【delegate】 的来历,它是:DefaultBeanDefinitionDocumentReader 类的成员变量,在不做提前配置的时候,它的默认值是:null
再看下图对 delegate 的初始化操作,所以前文的两个猜想,已经有了答案了,delegate 只有下图中唯一的一种类型。
对于自定义标签的解析,也只是在其上增加了一些配置,至于具体是什么样的配置,让 delegate 可以支持对,用户自定义标签的解析呢?
如下章节将围绕这个主题展开:

3 命名空间提取
我们基于,已知逻辑继续阅读代码:
delegate.parseCustomElement(ele); // 自定义命名空间(书签)

图中标注的3行代码表示如下3个行为:
3.1 从xml 元素获取命名空间uri
没啥好说的,就是简单xml文件内容中,关于自定标签的命名空间信息读取。
3.2 自定义标签 - 解析器获取
目光放到这里:
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
记住这个类名:[NamespaceHandler] 世界线会收束的,我们还会见到它的。
readerContext 是谁? 它从哪里来?
回顾下前文代码就知道了:
- 看看 红框那行代码的名字,世界又闭环了。

【tag:问题1】
NamespaceHandlerResolver.resolve() 干了啥?
【tag:问题1】
3.3 自定义标签内容的解析
再追踪 NamespaceHandler.parse(xx, xxx) 方法,我们发现,走不下去了:
【tag:问题2】
按下 【Ctrl + Alt + 鼠标左键】 发现不止一个类实现了NamespaceHandler接口,我们并不能确定下一步走向何方了。
【tag:问题2】

3.4 todo
为了解答 【3.2节】 和 【3.3节】 提出问题,在第四章中,我们将简单演示下,自定义标签的使用,看完第四章,这两个问题将得到初步解答。
4 自定义命名空间标签使用案例
同默认命名空间标签解析一样,这里只讨论从自定义标签,解析成 BeanDefinition 的过程
4.1 Bean定义
package org.springframework.custom.bean;
public class CustomModelBean {
private String modelName;
private String desc;
private String action;
public String getAction() {
return action;
}
public void setAction(String action) { this.action = action; }
public String getModelName() { return modelName; }
public void setModelName(String modelName) { this.modelName = modelName; }
public String getDesc() { return desc; }
public void setDesc(String desc) { this.desc = desc; }
}
4.2 自定义标签解析器 - 逻辑实现
package org.springframework.custom.parser;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
import org.springframework.custom.bean.CustomModelBean;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;
/**
* 继承抽象类 AbstractSingleBeanDefinitionParser 实现自定义标签的解析逻辑
* - 它会负责将xml 配置的标签解析成 BeanDefinition的形式,最终注册到 BeanFactory [ > BeanDefinitionRegistry]
*/
public class CustomModelParser extends AbstractSingleBeanDefinitionParser {
@Override
protected Class<?> getBeanClass(Element element) {
return CustomModelBean.class;
}
@Override
protected void doParse(Element element, BeanDefinitionBuilder builder) {
String modelName = element.getAttribute("modelName");
String desc = element.getAttribute("desc");
String action = element.getAttribute("action");
// 将从自定义标签提取到的属性注册到:BeanDefinitionBuilder;最终会注册到 BeanFactory中
if (StringUtils.hasText(modelName)) {
builder.addPropertyValue("modelName", modelName);
}
if (StringUtils.hasText(desc)) {
builder.addPropertyValue("desc", desc);
}
if (StringUtils.hasText(action)) {
builder.addPropertyValue("action", action);
}
}
}
4.3 将:自定义标签解析器 注册到 Spring 容器
这里,我们自定的标签名是:
- custom-model
我们可以在xml 文件里使用它去配置 自定义的Bean
package org.springframework.custom.handler;
import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
import org.springframework.custom.parser.CustomModelParser;
public class CustomModelHandler extends NamespaceHandlerSupport {
@Override
public void init() {
/**
* 注册自定义标签的解析器到 BeanFactory 中
*/
registerBeanDefinitionParser("custom-model", new CustomModelParser());
}
}
4.4 自定义标签声明:xsd 文件编辑
前文有提到过,spring 会通过xsd 或者 dtd 文件,对配置的xml进行校验。
如下的 xsd 文件,声明了,我们的自定义标签的属性;我们定义的:custom-bean 标签在被解析前,需要通过如下 xsd文件的校验。
【至于为啥要做校验,为了防止xml注入攻击应该是原因之一】
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- 这里的案例只是伪代码,这里贴的命名空间也是伪代码 -->
<xsd:schema xmlns="http://www.bokerr.com/schema/custom-bean"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.bokerr.com/schema/custom-bean"
elementFormDefault="qualified">
<xsd:element name="custom-bean">
<xsd:complexType>
<xsd:attribute name="id" type="xsd:string"/>
<xsd:attribute name="modelName" type="xsd:string"/>
<xsd:attribute name="desc" type="xsd:string"/>
<xsd:attribute name="action" type="xsd:string"/>
</xsd:complexType>
</xsd:element>
</xsd:schema>
4.5 编辑 Spring.handlers 和 Spring.schemas
在 spring.beans 模块下找到:Spring.handlers 和 Spring.schemas
我们这两个文件中追加我们自己的配置:

5 NamespaceHandler 解读
第四章所描述的就是我们的: custom-bean 标签的配置过程了。
第三章结尾的问题,也能答上来了,这里我们需要关注的 NamespaceHandler 的实现类是:
- NamespaceHandlerSupport 它是个抽象类
而我们在 CustomModelHandler 类中重写的就是 NamespaceHandlerSupport 的 init() 方法,我们在这里注入了,自定义标签的解析器:
- CustomModelParser
5.1 未完的问题-【问题1】: NamespaceHandlerResolver.resolve() 干了啥?
【Ctrl + Alt + 鼠标左键】 找到NamespaceHandlerResolver接口resolve方法唯一实现
getHandlerMappings 的代码就不放了,刚兴趣可以自己看.
里边的内容是:
从第四章中提到的:Spring.handlers 文件中,根据标签命名空间,
获取到我们自定义的:CustomModelHandler
- --> NamespaceHandlerSupport
- --> NamespaceHandler
- --> NamespaceHandlerSupport

5.2 未完的问题-【问题2】: NamespaceHandler().parse() 干了啥?
经过上一环节,这里我们拿到了 NamespaceHandler 继续深入 parse() 方法:
因为我们继承的 NamespaceHandlerSupport 抽象类,所以这里直接看 它的parse 方法

这里一共两件事:
5.2.1 提取自定义标签解析器
parser = findParserForElement(element, parserContext); 方法
提取通过 CustomModelHandler 注入容器的,自定义标签解析器:
- CustomModelParser
5.2.2 执行解析动作
(parser != null ? parser.parse(element, parserContext) : null)
当解析器不为空,执行解析的 parse 方法,自定义的 CustomModelParser 类覆写的超类方法是:
- doParse(Element element, BeanDefinitionBuilder builder)

根据 CustomModelParser 的类图,我们从而知道, parser.parse() 的实现逻辑在:
- AbstractBeanDefinitionParser.parse()

到此为止,调用到我们自己重写的 doParse() 方法了,
最终,上述的:parseInternal() 解析得到 BeanDefinition 后,会将其注册到 容器中。

这里的 parserContext 的来路也可以追溯。
至于 readerContext 的来路,我想不用我再说了吧,前文已经追溯过了,它打哪来:

自定义标签的学习也到此为止了。
《系列一》-- 5、xml配置文件解析之[自定义]命名空间[标签]的解析的更多相关文章
- 各种xml配置文件(所含内部标签及顺序)的提示功能是真的人性化
通过mybatis generator的配置文件来举例,其他配置文件(web.xml,mybatis,spring,springmvc等)同理 mybatis可以通过mybatis generator ...
- xml配置文件中常见的命名空间解释
1.1schema文档即xml schema document,schema文件的格式是.xsd(xml schema document的缩写xsd). 简单来说:schema就是对xml的进一步约束 ...
- maven的pom.xml配置文件中常用的配置标签解析(2018-03-13)
来自:https://www.cnblogs.com/Nick-Hu/p/7288198.html 拿过来记录下 <project xmlns="http://maven.apache ...
- Spring 系列教程之自定义标签的解析
Spring 系列教程之自定义标签的解析 在之前的章节中,我们提到了在 Spring 中存在默认标签与自定义标签两种,而在上一章节中我们分析了 Spring 中对默认标签的解析过程,相信大家一定已经有 ...
- Spring 系列教程之默认标签的解析
Spring 系列教程之默认标签的解析 之前提到过 Spring 中的标签包括默认标签和自定义标签两种,而两种标签的用法以及解析方式存在着很大的不同,本章节重点带领读者详细分析默认标签的解析过程. 默 ...
- 使用import简化spring的配置 spring import 标签的解析 使用import或加载spring配置时,报错误There is no ID/IDREF 多个Spring配置文件import resource路径配置
spring-import 标签的解析.使用案例: 对于spring配置文件的编写,我想,对于经历过庞大项目的人,都有那种恐惧的心理,太多的配置文件.不过,分模块都是大多数人能想到的方法,但是,怎么分 ...
- 基于Spring的可扩展Schema进行开发自定义配置标签支持
一.背景 最近和朋友一起想开发一个类似alibaba dubbo的功能的工具,其中就用到了基于Spring的可扩展Schema进行开发自定义配置标签支持,通过上网查资料自己写了一个demo.今天在这里 ...
- Spring源码分析(七)bean标签的解析及注册
摘要:本文结合<Spring源码深度解析>来分析Spring 5.0.6版本的源代码.若有描述错误之处,欢迎指正. 在上一篇中提到过Spring中的标签包括默认标签和自定义标签两种,而两种 ...
- spring源码深度解析— IOC 之 默认标签解析(上)
概述 接前两篇文章 spring源码深度解析—Spring的整体架构和环境搭建 和 spring源码深度解析— IOC 之 容器的基本实现 本文主要研究Spring标签的解析,Spring的标签 ...
- spring源码深度解析— IOC 之 默认标签解析(下)
在spring源码深度解析— IOC 之 默认标签解析(上)中我们已经完成了从xml配置文件到BeanDefinition的转换,转换后的实例是GenericBeanDefinition的实例.本文主 ...
随机推荐
- Raid卡型号的简单辨认与问题发现过程
Raid卡型号的简单辨认与问题发现过程 背景 同事给了一个awr报告. !-_-! 其实我看不懂. 但是他告诉我 有大量的log file sync 的等待事件. 然后又给了一套其他的文件, 发现lo ...
- [转帖]TiDB系统调参实战经验
https://tidb.net/blog/c9466c40#TiDB%E7%B3%BB%E7%BB%9F%E8%B0%83%E5%8F%82%E5%AE%9E%E6%88%98%E7%BB%8F%E ...
- [转帖]jumpserver 添加Windows主机
jump server添加 Windows主机资产 添加Linux主机资产步骤我们可以参照 链接:jump server添加Linux主机资产 进行操作. 一.资产管理-资产列表-创建资产 IP根据自 ...
- [转帖]a.out、coff、elf三种文件格式
补充:a.out早期并不是elf格式的,而是unix下另一种可执行格式,新的a.out是 本文讨论了 UNIX/LINUX 平台下三种主要的可执行文件格式:a.out(assembler and li ...
- 浅析RobotFramework工具的使用 | 京东物流技术团队
1 简介 最近几年越来越多的公司都开始进行自动化测试的设计和布局了,自动化,顾名思义就是把以人为驱动的测试行为转化为机器执行的一种过程,并经常用于回归测试中,市面上也存在很多开源的自动化测试的工具和理 ...
- 文盘Rust -- 领域交互模式如何实现
作者:京东科技 贾世闻 文盘Rust -- 领域交互模式如何实现 书接上文,上回说到如何通过interactcli-rs四步实现一个命令行程序.但是shell交互模式在有些场景下用户体验并不是很好.比 ...
- css伪类和伪元素在项目中的使用-红色*显示
CSS使用伪类给表单添加星号 <style type="text/css"> .form-item label::before { content: '*'; colo ...
- 【验证码逆向专栏】最新某验三代滑块逆向分析,干掉所有的 w 参数!
声明 本文章中所有内容仅供学习交流使用,不用于其他任何目的,不提供完整代码,抓包内容.敏感网址.数据接口等均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关! 本文章未经许 ...
- Gin 框架之Cookie与Session
目录 一.Cookie和Session的由来 二.Cookie简介 1. 什么是Cookie 2. Cookie规范 3. 安全性 4. Cookie 关键配置 三.Session简介 1. 什么是S ...
- 在mac中双击执行python
执行python脚本 mac有内置的python,但还是建议你自己安装一个python,如果没有卸载mac自带的python2.7,当你需要使用python3执行脚本时,python命令需要改为pyt ...