《系列一》-- 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的实例.本文主 ...
随机推荐
- [转帖]Web技术(四):TLS 握手过程与性能优化(TLS 1.2与TLS 1.3对比)
https://blog.csdn.net/m0_37621078/article/details/106126033?ops_request_misc=%257B%2522request%255Fi ...
- linux获取文件或者是进程精确时间的方法
linux获取文件或者是进程精确时间的方法 背景 很多时候需要精确知道文件的具体时间. 也需要知道进程的开始的精确时间. 便于进行一些计算的处理. 其实linux里面有很多方式进行文件属性的查看. 这 ...
- [转帖]PD Control 使用说明
https://docs.pingcap.com/zh/tidb/stable/pd-control PD Control 是 PD 的命令行工具,用于获取集群状态信息和调整集群. 安装方式 注意 ...
- [转帖]Alertmanager 部署配置
https://www.cnblogs.com/winstom/p/11940570.html 目录 前言 源码安装 配置 启动 配置prometheus监控Alertmanager 修改promet ...
- [转帖]45个处理字符串的Python方法
https://baijiahao.baidu.com/s?id=1738413163267646541&wfr=spider&for=pc 一.题目解析 先来看一个题目: 判断用 ...
- [转帖]exec系统调用
https://www.jianshu.com/p/3bf14f7d889c 进程的执行(exec) execve ececve系统调用可以将新程序加载到调用进程的内存空间,在这一过程中,将丢弃现有的 ...
- 简单的获取ESXi服务器上面开启了多少个vCPU的办法
开启ssh 执行命令 esxcli vm process list |grep Config |cut -b 17- |xargs cat |grep numvcpus |cut -d " ...
- Vue中is属性的用法 可以动态切换组件
is 是组件的一个属性,用来展示组件的名称 is和component联用哈 vue提供了component来展示对应的组件名称 compont是一个占位符,is这个属性,用来展示对应的组件名称 三个子 ...
- linux虚拟机固定ip
1.查看宿主机IP信息 在windows宿主机上,键盘输入win+r,输出cmd,打开终端命令行: 输入ipconfig /all,查看宿主机IP信息: 2.修改Linux虚拟机的配置文件 Linux ...
- Jupyter Notebook支持Go
在执行下列命令之前,请确保你已经安装了Go和Jupyter. gophernotes是针对Jupyter和nteract的Go内核,它可以让你在基于浏览器的笔记本或桌面app上交互式地使用Go.下面介 ...