阅读之前要注意的东西:本文就是主打流水账式的源码阅读,主导的是一个参考,主要内容需要看官自己去源码中验证。全系列文章基于 spring 源码 5.x 版本。

Spring源码阅读系列--全局目录.md

引子

1、容器最基本使用.md

系列1 - bean 标签解析:

2、XmlBeanFactory 的类图介绍.md

3、XmlBeanFactory 对xml文件读取.md

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

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配置文件解析之[自定义]命名空间[标签]的解析的更多相关文章

  1. 各种xml配置文件(所含内部标签及顺序)的提示功能是真的人性化

    通过mybatis generator的配置文件来举例,其他配置文件(web.xml,mybatis,spring,springmvc等)同理 mybatis可以通过mybatis generator ...

  2. xml配置文件中常见的命名空间解释

    1.1schema文档即xml schema document,schema文件的格式是.xsd(xml schema document的缩写xsd). 简单来说:schema就是对xml的进一步约束 ...

  3. maven的pom.xml配置文件中常用的配置标签解析(2018-03-13)

    来自:https://www.cnblogs.com/Nick-Hu/p/7288198.html 拿过来记录下 <project xmlns="http://maven.apache ...

  4. Spring 系列教程之自定义标签的解析

    Spring 系列教程之自定义标签的解析 在之前的章节中,我们提到了在 Spring 中存在默认标签与自定义标签两种,而在上一章节中我们分析了 Spring 中对默认标签的解析过程,相信大家一定已经有 ...

  5. Spring 系列教程之默认标签的解析

    Spring 系列教程之默认标签的解析 之前提到过 Spring 中的标签包括默认标签和自定义标签两种,而两种标签的用法以及解析方式存在着很大的不同,本章节重点带领读者详细分析默认标签的解析过程. 默 ...

  6. 使用import简化spring的配置 spring import 标签的解析 使用import或加载spring配置时,报错误There is no ID/IDREF 多个Spring配置文件import resource路径配置

    spring-import 标签的解析.使用案例: 对于spring配置文件的编写,我想,对于经历过庞大项目的人,都有那种恐惧的心理,太多的配置文件.不过,分模块都是大多数人能想到的方法,但是,怎么分 ...

  7. 基于Spring的可扩展Schema进行开发自定义配置标签支持

    一.背景 最近和朋友一起想开发一个类似alibaba dubbo的功能的工具,其中就用到了基于Spring的可扩展Schema进行开发自定义配置标签支持,通过上网查资料自己写了一个demo.今天在这里 ...

  8. Spring源码分析(七)bean标签的解析及注册

    摘要:本文结合<Spring源码深度解析>来分析Spring 5.0.6版本的源代码.若有描述错误之处,欢迎指正. 在上一篇中提到过Spring中的标签包括默认标签和自定义标签两种,而两种 ...

  9. spring源码深度解析— IOC 之 默认标签解析(上)

    概述 接前两篇文章  spring源码深度解析—Spring的整体架构和环境搭建  和  spring源码深度解析— IOC 之 容器的基本实现 本文主要研究Spring标签的解析,Spring的标签 ...

  10. spring源码深度解析— IOC 之 默认标签解析(下)

    在spring源码深度解析— IOC 之 默认标签解析(上)中我们已经完成了从xml配置文件到BeanDefinition的转换,转换后的实例是GenericBeanDefinition的实例.本文主 ...

随机推荐

  1. [转帖]ORA-01450 maximum key length (3215) exceeded

    一. 问题背景 给一个业务表online建索引时遇到了ORA-01450 maximum key length (3215) exceeded报错,看字面意思是字段太长了,检查表字段类型发现基本都是n ...

  2. [转帖]Oracle 23c 才支持 TLS1.3

    Transport Layer Security 1.3 Protocol Now Supported in Oracle Database Starting with Oracle Database ...

  3. 【转帖】nginx变量使用方法详解-2

    https://www.diewufeiyang.com/post/576.html 关于 Nginx 变量的另一个常见误区是认为变量容器的生命期,是与 location 配置块绑定的.其实不然.我们 ...

  4. 【图论】CF1508C Complete the MST

    Problem Link 有一张 \(n\) 个点的完全图,其中 \(m\) 条边已经标有边权.你需要给剩下的边都标上权值,使得所有边权的异或和为 \(0\),并且整张图的最小生成树边权和最小. \( ...

  5. 感性理解 int 与 long long 的速度差距 & 感性理解不同取模方法的差距

    long long 题该怎么做?#define int long long 会多慢? 有时候,当我们被卡常的时候,不妨想一想,自己在开头定义的 #define int long long 有多大影响? ...

  6. 手写一个Promise完成resolve 和 reject状态的改变和修改属性

    1.手写 Promise 1 创建一个文件 Promise.js:内容 function Promise(){ } 2 引入 Promise.js 这个文件 <script src=" ...

  7. 【JS 逆向百例】有道翻译接口参数逆向

    逆向目标 目标:有道翻译接口参数 主页:https://fanyi.youdao.com/ 接口:https://fanyi.youdao.com/translate_o?smartresult=di ...

  8. 【JS 逆向百例】DOM事件断点调试,某商盟登录逆向

    声明 本文章中所有内容仅供学习交流,抓包内容.敏感网址.数据接口均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关,若有侵权,请联系我立即删除! 逆向目标 目标:某商盟登录 ...

  9. 基于新浪微博海量用户行为数据、博文数据数据分析:包括综合指数、移动指数、PC指数三个指数

    基于新浪微博海量用户行为数据.博文数据数据分析:包括综合指数.移动指数.PC指数三个指数 项目介绍 微指数是基于海量用户行为数据.博文数据,采用科学计算方法统计得出的反映不同事件领域发展状况的指数产品 ...

  10. 人工智能自然语言处理:N-gram和TF-IDF模型详解

    人工智能自然语言处理:N-gram和TF-IDF模型详解 1.N-gram 模型 N-Gram 是一种基于统计语言模型的算法.它的基本思想是将文本里面的内容按照字节进行大小为 N 的滑动窗口操作,形成 ...