Table of Contentsgenerated with DocToc[1]
•官方例子[2]•自定义标签使用[3]
•定义普通的 POJO 组件[4]•定义 XSD
描述文件[5]•定义组件解析器[6]•创建处理类的注册器[7]•编写 spring.hanlders
和 spring.schemas
文件[8]•使用 Demo[9]
•配置文件[10]•测试代码[11]
•小结[12]
•自定义标签解析[13]
•① 获取标签的命名空间[14]•② 根据命名空间找到对应的 NamespaceHandler[15]•③ 调用自定义的 NamespaceHandler 进行解析[16]
•总结[17]•参考资料[18]
又来填坑啦,上一篇讲完默认标签的解析,这篇笔记记录一下自定义标签的解析吧。
我们知道,Spring
源码的核心模块是 Spring-core
和 Spring-beans
,在此基础上衍生出其他模块,例如 context
、 cache
、 tx
等模块,都是根据这两个基础模块进行扩展的。
聪明如你,应该想到我们代码中常用的缓存注解 @Cacheable
、事务注解 @Transaction
,还有阿里巴巴的 RPC
中间件 Dubbo
,在配置文件中通过 <dubbo:service/>
或者 <dubbo:reference/>
进行服务注册和订阅,这些都都属于 Spring
的自定义标签的实现,通过自定义标签可以实现更加强大的功能!
作为一个有追求的程序员,当然不能满足于框架自带默认的标签,为了扩展性和配置化要求,这时候就需要学习自定义标签和使用自定义标签~
官方例子
先来看一张源码图片(红框框圈着是重点哟)
刚才说了缓存和事务,那就拿这两个举例,还有一个标签 <myname:>
(这个我也不太清楚,网上查的资料也不多,所以按照我的理解大家跟说下)
首先我们看到,<tx>
<cache>
<mvc>
和 <myname>
都是自定义标签,左一是配置文件,进行 bean
的定义,顶部的 xmlns
是命名空间,表示标签所属的定义文件,像事务、缓存、MVC
的命名空间都是固定的。
而 myname
相当于万金油,既可以定义为事务,又可以定义为缓存,只要我们在命名空间中进行相应的定义就能正确的识别。这个就是我们待会要使用到的自定义标签,通过命名空间定位到我们想要的处理逻辑。
中间的是缓存定义的 xsd
文件,通过 <xsd:element name="annotation-driven">
定义元素,<xsd:complexType>
区间内定义属性列表,<xsd:attribute>
定义单个属性,详细分析可以看下注释~
右边的是事务定义的 xsd
文件,大体内容的跟中间一样,虽然元素名称 <annotation-driven>
有相同的,但是下面的属性定义是有所区别的。
所以我们对自定义注解有个大概的了解,xsd
描述文件是个其中一个关键,在配置文件顶部的命名空间是标签进行解析时,进行定位的配置,当然还有处理器,下面使用时进行介绍。
不知道理解的对不对,如果有误的话请大佬们指出,我会进行修改的!
自定义标签使用
Spring
提供了可扩展的 Schema
的支持,扩展 Spring
自定义标签配置需要以下几个步骤:
•创建一个需要扩展的组件•定义一个 XSD
描述文件•创建一个文件,实现 BeanDefinitionParse
接口,用来解析 XSD
文件中的定义和组件定义。•创建一个 Handler
文件,扩展自 NamespaceHandlerSupport
,将组件注册到 Spring
容器•编写 Spring.handlers
和 Spring.schemas
文件
刚开始看到这些流程时,我还是有点慌的,毕竟从一个使用默认标签的萌新小白,突然要我自己定义,感觉到很新鲜,所以请各位跟着下面的流程一起来看吧~
定义普通的 POJO 组件
这个没啥好说的,就是一个普通的类:
public class Product { private Integer productId; private String unit; private String name; }
定义 XSD
描述文件
custom-product.xsd
<xsd:schema targetNamespace="http://vip-augus.github.io/schema/product" xmlns:xsd="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified"> <!-- 注释 3.4 自定义元素 --> <xsd:element name="product"> <xsd:complexType> <!-- 这个是类注册时的名字,组件中请不要占用该字段~ --> <xsd:attribute name="id" type="xsd:string"/> <!-- 属性定义列表,名字和类型 --> <xsd:attribute name="productId" type="xsd:integer"/> <xsd:attribute name="unit" type="xsd:string"/> <xsd:attribute name="name" type="xsd:string"/> </xsd:complexType> </xsd:element> </xsd:schema>
我在上面的描述文件中,定义了一个新的 targetNamespace
,同时定义了一个 叫 product
的新元素,并且将组件中的属性都列在 <xsd:attribute>
中。XSD
文件是 XML
DTD
的替代者,具体就不多深入,感兴趣的同学可以继续深入了解。
定义组件解析器
base.label.custom.ProductBeanDefinitionParser
public class ProductBeanDefinitionParser extends AbstractSingleBeanDefinitionParser { @Override protected Class getBeanClass(Element element) { // 返回对应的类型 return Product.class; } // 从 element 中解析并提取对应的元素 @Override protected void doParse(Element element, BeanDefinitionBuilder builder) { String productId = element.getAttribute("productId"); String productName = element.getAttribute("name"); String productUnit = element.getAttribute("unit"); // 将提取到的数据放入 BeanDefinitionBuilder 中,等到完成所有 bean 的解析之后统一注册到 beanFactory 中 if (productId != null) { // element.getAttribute("") 方法取出来的都是 string 类型,使用时记得手动转换 builder.addPropertyValue("productId", Integer.valueOf(productId)); } if (StringUtils.hasText(productName)) { builder.addPropertyValue("name", productName); } if (StringUtils.hasText(productUnit)) { builder.addPropertyValue("unit", productUnit); } } }
关键点在于,我们的解析器是继承于 AbstractSingleBeanDefinitionParser
,重载了两个方法,详细用途请看注释~
创建处理类的注册器
base.label.custom.ProductBeanHandler
public class ProductBeanHandler extends NamespaceHandlerSupport { @Override public void init() { // 将组件解析器进行注册到 `Spring` 容器 registerBeanDefinitionParser("product", new ProductBeanDefinitionParser()); } }
这个类也比较简单,关键是继承了 NamespaceHandlerSupport
,对他进行了扩展,在该类初始化时将组件解析器进行注册到 Spring
容器中。
编写 spring.hanlders
和 spring.schemas
文件
我将文件位置放在 resources
-> META-INF
目录下:
spring.handlers
http\://vip-augus.github.io/schema/product=base.label.custom.ProductBeanHandler
spring.schemas
http\://vip-augus.github.io/schema/product.xsd=custom/custom-product.xsd
到了这一步,自定义的配置就结束了。下面是如何使用