Spring如何实现自定义名称空间的解析
小橘子🍊

本文的例子参考 spring拓展之定义自己的namespace

前言

今年的春招已经接近尾声了,因为自己没有实际的项目经验,所以找实习的情况很不理想,心里当然是很不甘心的,所以就开始盘算着要动手做出点实际的东西出来,这样今年的秋招才会有底气。先想到的就是自己开发出一个类似于 dubbo 的 rpc 框架,能够实现基本的功能就好。

为了实现这个小目标,最开始当然是先看看 dubbo 的源码来了解下它的实现原理,因为 dubbo 是依赖于 spring 的 ioc 功能来实现服务的发布和调用的,所以最开始就得先解决 dubbo 自定义标签的解析问题,本文就是关于一个最简单的自定义标签解析的实现。

实现自定义标签的解析

为了实现自定义标签的解析,我们需要定义:

  • xsd 文件:这个文件用于对自定标签的命名空间规则进行约束。

  • spring.handlers 文件:用于指定自定义标签的命名空间的处理器。

  • spring.schemas 文件:指定相应命名空间的 xsd 约束文件的位置。

这三个文件需要放在 classpath 根目录的 META-INF 文件夹目录之下,在定义好这三个文件后,就可以测试自定义的标签的解析了。这么说比较抽象,还是举个栗子进行说明。

定义一个 xsd 文件

定义一个 xsd 文件来对自定义命名空间的标签进行约束,因为 xsd 文件的编写规则我们不是很清楚,所以要想实现更加复杂的约束,就需要深入的去学习 xml 和 xsd 规则,这里给个学习地址

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<xsd:schema xmlns="https://www.aprilyolies.top/schema/beehive"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="https://www.aprilyolies.top/schema/beehive">

<xsd:import namespace="http://www.w3.org/XML/1998/namespace"/>
<xsd:import namespace="http://www.springframework.org/schema/beans"/>
<xsd:import namespace="http://www.springframework.org/schema/tool"/>

<xsd:annotation>
<xsd:documentation><![CDATA[ Namespace support for the beehive test. ]]></xsd:documentation>
</xsd:annotation>

<xsd:complexType name="mybeanType">
<xsd:attribute name="id" type="xsd:ID">
<xsd:annotation>
<xsd:documentation><![CDATA[ The unique identifier for a bean. ]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="name" type="xsd:string" use="required">
<xsd:annotation>
<xsd:documentation><![CDATA[ The mybean name. ]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="class" type="xsd:string" use="required">
<xsd:annotation>
<xsd:documentation><![CDATA[ The version. ]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>


<xsd:element name="mybean" type="mybeanType">
<xsd:annotation>
<xsd:documentation><![CDATA[ The mybean config ]]></xsd:documentation>
</xsd:annotation>
</xsd:element>
</xsd:schema>

spring.handlers 文件

spring.handlers 文件用于指定自定义命名空间的处理器类。

1
https\://www.aprilyolies.top/schema/beehive=com.aprilyolies.beehive.BeehiveNamespaceHandler

spring.schemas 文件

spring.schemas 文件用于指定相应命名空间约束文件的位置。

1
https\://www.aprilyolies.top/schema/beehive.xsd=META-INF/beehive.xsd

上述的三个文件的存放位置十分讲究,请参看下图:

测试自定义命名空间

编写 spring 的配置文件

这是启动 spring ioc 容器的配置文件,熟悉 spring 使用的小伙伴都不会陌生。注意这里的 beehive 就是我们自定义的命名空间,而 mybean 就是我们自定义的标签。

1
2
3
4
5
6
7
8
9
10
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beehive="https://www.aprilyolies.top/schema/beehive"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
https://www.aprilyolies.top/schema/beehive https://www.aprilyolies.top/schema/beehive.xsd">

<beehive:mybean class="com.aprilyolies.beehive.People" id="people" name="eva"/>

</beans>

自定义命名空间的处理器

创建一个用于解析自定义命名空间的类,这个类继承了 NamespaceHandlerSupport 类,我们只需要实现 init 方法的逻辑,来向 spring 容器注册不同标签的解析器,因为我们在 xsd 文件中,只是定义了一个 mybean 标签,所以这里只需要注册一个解析器就行了。

1
2
3
4
5
6
7
8
9
10
11
12
13
import org.springframework.beans.factory.xml.NamespaceHandlerSupport;

/**
* @Author EvaJohnson
* @Date 2019-05-13
* @Email g863821569@gmail.com
*/
public class BeehiveNamespaceHandler extends NamespaceHandlerSupport {
@Override
public void init() {
registerBeanDefinitionParser("mybean", new MybeanParser());
}
}

编写标签解析器

这个解析器就是上文中提到的 mybean 标签解析器,用于将 mybean 标签解析为可以由 spring 容器管理的 bean。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* @Author EvaJohnson
* @Date 2019-05-13
* @Email g863821569@gmail.com
*/
public class MybeanParser implements BeanDefinitionParser {
@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {

RootBeanDefinition mbd = new RootBeanDefinition();
mbd.setBeanClassName(element.getAttribute("class"));
String beanName = element.getAttribute("id");
MutablePropertyValues mutablePropertyValues = new MutablePropertyValues();
mutablePropertyValues.addPropertyValue("name", element.getAttribute("name"));
mbd.setPropertyValues(mutablePropertyValues);
parserContext.getRegistry().registerBeanDefinition(beanName, mbd);

return mbd;
}
}

pojo 类

没啥好解释,用于功能测试,省略了 getter 和 setter 方法。

1
2
3
4
5
6
7
8
9
10
/**
* @Author EvaJohnson
* @Date 2019-05-13
* @Email g863821569@gmail.com
*/
public class People {
private String id;
private String name;
private Integer age;
}

App 类

执行这个 App 类的 main 方法,如果能正常输出 People 类的 name 属性,则表示自定义的命名空间及标签解析成功了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* @Author EvaJohnson
* @Date 2019-05-13
* @Email g863821569@gmail.com
*/
public class App {
public static void main(String[] args) {
ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext(new String[]{"beehive.xml"}, false);

ac.refresh();

People bean = (People) ac.getBean("people", People.class);

System.out.println(bean.getName());
}
}

总结

本文中举的例子可以说是非常简单的,所以涉及的标签解析过程并不复杂,但是如果是用于生产环境的话,那么标签的定义就会比较复杂,相应的 xsd 文件内容也会比较多。我自己对于 xsd 的编写规则也是一片空白,有时间还是要了解一下相关的知识,毕竟我自己考虑的一个类似于 dubbo 的 rpc 框架也是需要依赖于 spring 的 ioc 容器的,如果这个 xsd 文件不会写,那就没办法开始了对吧。