在网络传输中JSON和XML是最长用的两种数据格式,JSON的特点是短小、简单,但是除了这点以外就完全不能跟XML比了,所以涉及到配置方面还是优先考虑XML吧!
但是裸奔的XML并不好用,比如我们打出来Jar包给别人用,需要他们自己在Spring配置中添加:
<bean class="xxxxxx"/>
功能简单的时候是没有问题的,当你做的东西比较复杂的时候就会变成:
<bean class="xxx">
<property name="aaa" value="aaa"/>
<property name="bbb" value="bbb"/>
<property name="ccc" value="ccc"/>
</bean>
除非在你的WILE里面写的非常清楚应用用哪个class,需要设置哪些property,哪些是必填的等等等,不然没人知道该怎么写,而更好的解决办法是编写schema来定义XML的规则!
我们在配置Spring的时候经常会这么写:
<beans:beans xmlns:beans="http://www.springframework.org/schema/beans">
<beans:import resource="xxx"/>
</beans:beans>
其中http://www.springframework.org/schema/beans就是一个命名空间,而xmlns:beans相当于设置了命名空间的一个代号,在使用时beans:import就可以表示使用该命名空间中的import元素。
可以不写:beans来表示默认就用该命名空间,那么配置就更简单了:
<beans xmlns="http://www.springframework.org/schema/beans">
<import resource="xxx"/>
</beans>
在schema中由下面三个属性来控制命名空间的行为:
当设置unqualified时schema中除了根元素以外,其他的元素都是没有命名空间的,在使用的时候需要将其命名空间设置为空:
<easydt:easydt xmlns:easydt="http://www.cainiao.com/schema/easydt">
<provider xmlns=""/><!-- 注意这里 -->
</easydt:easydt>
而设置为qualified时schema中定义的所有元素都属于targetNamespace所定义的命名空间:
<easydt:easydt xmlns="http://www.cainiao.com/schema/easydt">
<provider/><!-- 看这里 -->
</easydt:easydt>
显然用qualified看起来更简单一些,不过也是看情况的。
完整的schema的定义如下:
<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.w3school.com.cn"
xmlns="http://www.w3school.com.cn"
elementFormDefault="qualified">
在这里定义元素和属性
</xs:schema>
其目的就是配置出来一堆的element和attribute来约束XML的行为,简单来说
<yyy xxx="xxx"/>
其中:yyy是element、xxx是属性!最简单的元素如下:
<easydt:a>123</easydt:a>
对应的配置如下:
<xs:element name="a" type="xs:integer"/>
设置type为integer之后会对内容进行检查,如果不是数字则报错,另外可以通过simpleType对其扩展来实现更复杂的限定:
<xs:element name="age">
<xs:simpleType>
<xs:restriction base="xs:integer">
<xs:minInclusive value="0"/>
<xs:maxInclusive value="100"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
向元素中添加子元素、属性之后就不是一个简单元素,而是一个复杂元素,可以用complexType定义其类型:
<xs:element name="note">
<xs:complexType>
<xs:attribute name="app" type="xs:string"/>
</xs:complexType>
</xs:element>
对应的XML的配置为<easydt:note app="123"/>,子节点的定义也很简单:
<xs:element name="note">
<xs:complexType>
<xs:sequence>
<xs:element name="a" type="xs:integer"/>
<xs:element name="b" type="xs:integer"/>
</xs:sequence>
</xs:complexType>
</xs:element>
对应的XML的配置为<note><a>1</a><b>2</b></note>,其中sequence的作用是
组中的元素以指定的顺序出现在包含元素中,每个子元素可以出现0次到任意次
当然还有其他的方式:
| 指示器 | 含义 |
|---|---|
| all | 子元素可以按照任意顺序出现,且每个子元素必须只出现一次 |
| choice | 随便添加子元素,可以使用maxOccurs来设置可添加子元素的数目 |
| attributeGroup | 属性组 |
| group | 元素组 |
元素的类型是非常复杂的,不同的类型之间很可能有一些定义是可以重用的,我们可以定义一些基础的类型,然后使用extension对其进行扩展可以得到:
<xs:complexType name="baseInfo">
<xs:sequence>
<xs:element name="id" type="xs:string"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="fullpersoninfo">
<xs:complexContent>
<xs:extension base="baseInfo">
<xs:sequence>
<xs:element name="name" type="xs:string"/>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
其他元素的可以在这里查看使用方法~~
当上面这些不能满足你的需求时,可以使用any、anyAttribute来允许用户配置没有在schema中定义过的东西,然后在解析的阶段进行处理!
在Spring中定义解析需要用下面两个文件来配置(需要放在META-INF目录,Spring会自动加载):
来看个例子:
// spring.schemas http\://www.cainiao.com/schema/easydt/easydt.xsd=META-INF/easydt.xsd // spring.handlers http\://www.cainiao.com/schema/easydt=com.cainiao.easydt.client.springTag.EasyDtNamespaceHandler
在NamespaceHandlerSupport中定义了遇到对应的元素的时候应该使用Parser:
public class EasyDtNamespaceHandler extends NamespaceHandlerSupport {
public void init() {
registerBeanDefinitionParser("easydt", new EasyDtBeanDefinitionParser());
}
}
然后用AbstractBeanDefinitionParser中拿到配置信息并使用addPropertyValue来定义BeanDefinition:
public class EasyDtBeanDefinitionParser extends AbstractSingleBeanDefinitionParser{
protected Class<EasyDt> getBeanClass(Element element) {
return EasyDt.class;
}
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
builder.addPropertyValue("domain", element.getAttribute("domain"));
}
}
关于BeanDefinition的载入和解析的过程可以看这里,具体的解析工作是交给BeanDefinitionParserDelegate来完成的,如果子元素不是简单元素可以调用parseCustomElement来完成解析:
builder.addPropertyValue("provider",
parserContext.getDelegate().parseCustomElement(
DomUtils.getChildElementByTagName(element, "provider"),
builder.getRawBeanDefinition()));
想更灵活地在Spring中玩耍XML还是要多看看Bean的解析过程。
用这些最基本的用法基本可以搞定大部分的自定义schema的需求,对于复杂的还需要深入去研究。