赵玉伟的博客

xml名称空间

概述

xml,即可扩展标记语言, 英文名称为: Extend Markup Language; xml的一个师兄是html,即超文本标记语言(这个地方翻译成富文本标记语言更能准确的表达语义),Hypertext Markup Language; 这两种语言都是需要标记的,标记可以简单理解成以下的语法形式

1
<mark></mark>

用这种形式, 可以用某些特殊的标记,让原本没有意义的文本具有明确的含义。比如:

1
<title>hello world</title>

此处的hello world就被title标记成了一个标题;

可扩展,指的是在某个文档中,再增加部分标记,不会影响到原有的标记。比如:

1
2
3
<title>hello world</title>
<name>zhang san</name>
<age>19</age>

在原有title的标记的基础上, 可以再增加(扩展)其他的标记元素,原有的标记不受影响。

html是不能扩展的,因为html可以识别的元素, 已经被写入到主流的浏览器厂商写入到浏览器中, 浏览器只能识别和认识某一些标记,比如: html, head, body, title……。

xml的作用

1、xml可以用来传输数据,比如,soap协议就是用xml传递数据;但是, 由于xml需要大量的标记来对数据进行“包围”,这就导致传输1K的数据,真正有用的数据可能只有600Byte, 另外400Byte被标记占用。 那么,在被序列化时,以及被传输时, 都造成了浪费。 JSON完美的解决了这个问题,json除了有用的数据外, 仅仅多了一点点的标点符号, 传输的效率大大提高, 几乎没有空间的浪费。
比如, 现在A,B需要进行通信, 约定了通过以下格式

1
2
3
4
5
<person>
<name>张三</name>
<age>19</age>
<sex>f</sex>
</person>

那么,只要双方把使用的标记约定好,也就是双方都认识并且明确每个标记的意思, 传输的信息就可以互通。

2、也可以用来标识某些数据的含义,也是目前用的最多的, 比如,几乎所有的框架, 都离不开xml配置, 因为xml可以表达足够丰富的信息, 某个xml标记元素可以有属性,比如:

1
<bean id="helloworld" class="com.test.HelloWorld"></bean>

标记可以有子元素

1
2
3
<bean id="helloworld" class="com.test.HelloWorld">
<property>......</property>
</bean>

3、也可以用xml存储数据,但是很少用。

用xml的schema验证xml文件的正确性

用xml作为配置的标记语言, 假如只被一个人用,可以口头通知每个配置的含义与用法; 如果用在框架中, 框架会被无数的人或组织使用, 配置作为使用者和框架交互的窗口, 框架必须保证每个人配置的正确性。 怎么保证? 提供一个dtd文件,或xsd(xml schema definition)文件, 用来明确定义每个标记的用法与意义。 当某个xml文件中用到该标记时, 指定这个标记的验证文件, 当加载这个xml文件时, 解析器会用这个验证文件去验证标记。由于dtd文件的语法、所支持的数据类型等方面存在不足,而xsd文件也是一个xml格式的文件,所以, 能用xsd的地方, 几乎都用xsd来验证xml文件, xsd文件也就是xml的meta。
当一个框架被启动时,我们放在xml文件中的配置, 会被指定的xsd文件校验, 这一步很重要,需要严格校验,因为配置是使用者和框架交互的窗口,框架使用者往往只需要关注配置就可以使用,一旦配置出错,框架无法识别, 会导致框架初始化失败。
怎么验证? jdk提供了一套API, 用来完成验证。可以参考以下几个包:

1
2
3
4
org.xml.sax
javax.xml
org.w3c.dom
com.sun.org.apache.xerces.internal

#xml配置的几种写法
以spring为例, 先看一个正确的xml配置

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
<?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:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
<context:component-scan base-package="com.yuwei.test" />
<bean id="log4jInitialization" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetClass" value="org.springframework.util.Log4jConfigurer" />
<property name="targetMethod" value="initLogging" />
<property name="arguments" value="classpath:config/log4j.properties" />
</bean>
<bean
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:config/*.properties</value>
</list>
</property>
</bean>
<aop:aspectj-autoproxy proxy-target-class="true" />
</beans>

与其等价的另外一种配置方式

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
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
<context:component-scan base-package="com.yuwei.test " />
<beans:bean id="log4jInitialization" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<beans:property name="targetClass" value="org.springframework.util.Log4jConfigurer" />
<beans:property name="targetMethod" value="initLogging" />
<beans:property name="arguments" value="classpath:config/log4j.properties" />
</beans:bean>
<beans:bean
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<beans:property name="locations">
<beans:list>
<beans:value>classpath:config/*.properties</beans:value>
</beans:list>
</beans:property>
</beans:bean>
<aop:aspectj-autoproxy proxy-target-class="true" />
</beans:beans>

如果想写的拉风一点, 还可以这么玩

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
<?xml version="1.0" encoding="UTF-8"?>
<hello:beans xmlns:hello="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
<context:component-scan base-package="com.yuwei.test " />
<hello:bean id="log4jInitialization" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<hello:property name="targetClass" value="org.springframework.util.Log4jConfigurer" />
<hello:property name="targetMethod" value="initLogging" />
<hello:property name="arguments" value="classpath:config/log4j.properties" />
</hello:bean>
<hello:bean
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<hello:property name="locations">
<hello:list>
<hello:value>classpath:config/*.properties</hello:value>
</hello:list>
</hello:property>
</hello:bean>
<aop:aspectj-autoproxy proxy-target-class="true" />
</hello:beans>

命名空间只是看上去是一个url的特殊字符串

先说一下xmlns, 即xml name space, 命名空间的作用用来划定某些标记的所属范围,在一个xml文档中, 不同范围的相同名称的标记,是不会产生冲突的。 xmlns的值往往是一个“url地址”,使用url的原因在于全球的域名是唯一的, 所以,命名空间也就不容易重复, 这个“url地址” 能否访问不重要, 他仅仅看上去像是一个url,实际意义是用类似url的语法编写的一段字符串,保证命名空间的唯一性。
在命名空间中(xsd文件中描述了一批元素的定义与用法, 然后会把这一批元素用 targetNamespace 装到一个命名空间中),定义了这个命名空间内包含的元素与用法, 这个知识点可以参考*.xsd文件;作为使用者,使用名称空间的方式如下:

xmlns:{前缀,或者叫做名称空间的别名}=”真正的命名空间,在某个xsd文件中已经定义好”

demo如下:

1
xmlns:context="http://www.springframework.org/schema/context"

这句话表达的意思是: 在 标记的范围内, 用context作为命名空间 http: //www.springframework.org/schema/context的别名,这样的好处是 简单, 容易理解,当用到http: //www.springframework.org/schema/context这个命名空间的元素时, 便可以通过这种方式使用。如果没用声明xmlns:context=”http://www.springframework.org/schema/context", 但是却用到了,因为无法识别 context, 而且也不知道component-scan 属于哪个命名空间, 会导致解析出错。

1
2
3
<xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">

同样,schemaLocation是命名空间xsi的一个元素, 只不过这个元素的值是成对出现的, 用来指明 某个命名空间的元素,用哪一个xsd文件验证, 所以,要成对出现, 中间用空格分割。

对于某个用于校验的xsd文件,比如spring-aop-3.0.xsd:

1
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd

虽然可以通过url访问这个文件, 但是通常这个xsd文件是随着框架打包的, 所以, 在不联网的情况下,也可以完成校验。
有时候, 在启动spring的时候, 可能会报xml解析失败, 这一般是由于xml中用到的版本和解析xml的xsd版本不对应导致的。

默认命名空间

一般会指定一个默认命名空间, 以减少繁冗的重复前缀,比如

1
<beans xmlns="http://www.springframework.org/schema/beans"

这句话表达的意思是,默认的名称空间为 http: //www.springframework.org/schema/beans , 也就是在beans元素内的标签,如果没有明确指定元素属于哪个名称空间, 那么就把默认的名称空间作为该元素的名称空间。所以, 都是属于 http: //www.springframework.org/schema/ beans 名称空间下的元素, 这样, 在大量使用元素的时候, 就不用写前缀了; 在我们上面给出的三种写法中,第二种和第三种,都是因为没有默认名称空间,导致需要写大量的前缀, 可读性不是很友好。

xml schema

xml schema 也是一个xml文件,只不过作用是用来构建命名空间,并将元素和属性添加其中。有自己的一套语法,以下的demo, 一个是构建beans空间的xsd, 一个是构建aop空间的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
<xsd:schema xmlns="http://www.springframework.org/schema/beans"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.springframework.org/schema/beans">
<!-- 省略 -->
<xsd:attribute name="default-autowire" default="default">
<xsd:annotation>
<xsd:documentation><![CDATA[
The default 'autowire' value; see the documentation for the
'autowire' attribute of the 'bean' element. The default is 'default'.
]]></xsd:documentation>
</xsd:annotation>
<xsd:simpleType>
<xsd:restriction base="xsd:NMTOKEN">
<xsd:enumeration value="default"/>
<xsd:enumeration value="no"/>
<xsd:enumeration value="byName"/>
<xsd:enumeration value="byType"/>
<xsd:enumeration value="constructor"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:attribute>
<!-- 省略 -->
</xsd:schema>

1
2
3
4
5
6
7
8
9
10
<xsd:schema xmlns="http://www.springframework.org/schema/aop"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:tool="http://www.springframework.org/schema/tool"
targetNamespace="http://www.springframework.org/schema/aop"
elementFormDefault="qualified"
attributeFormDefault="unqualified">
<!-- 省略 -->
</xsd:schema>

根据对命名空间的分析,我们知道以xsd作为前缀的元素,都存在于 xmlns:xsd=”http://www.w3.org/2001/XMLSchema“ 这个命名空间中, 为什么命名空间一定要指定为 http://www.w3.org/2001/XMLSchema 呢?
因为这是w3c的规范, 这个规范标明了我们的xml schema当前遵守的是w3c的规范, 而不是其他的规范; 同时, 在这个命名空间中,也定义了最基本的构建材料, 可以在浏览器中输入
http://www.w3.org/2001/XMLSchema.xsd
这个url查看。 在以下的参考链接中,有一张类似于继承链的图, 表达的很形象; 关于 targetNamespace, 就是指明描述的元素属于这个命名空间。

结论

如果我们写一个框架、 或者一个插件,提供出来给第二方或者第三方使用的时候,如果需要xml配置,是需要进行校验的,这个时候需要写xsd。作为使用者, 虽然不用写这些,但是有必要明白名称空间的来龙去脉; 当我们在去看一些源码的时候, 也能够提供很好的帮助。

参考
http://www.oracle.com/technetwork/cn/articles/srivastava-namespaces-098626-zhs.html