XML 命名空间支持

Apache Ant 1.6 引入了对 XML 命名空间的支持。

历史

Ant 1.6 之前的 Ant 版本都不支持 XML 命名空间。不支持基本上意味着两件事:

过去一直不鼓励在元素名称中使用冒号,并且 XML 规范实际上强烈建议不要使用以 xml 开头的任何属性,以保留这些名称以供将来使用。

动机

在使用大量自定义和第三方任务的构建文件中,很容易出现名称冲突。在定义单个类型时,构建文件编写者可以手动进行一些命名空间处理(例如,使用 tomcat-deploy 而不是 deploy)。但是,当使用 <typedef>resource 属性定义整个类型库时,构建文件编写者没有机会覆盖或甚至为库提供的名称添加前缀。

分配命名空间

<typedef> 添加 prefix 属性可能已经足够了,但 XML 已经有一个众所周知的命名空间方法。因此,与其添加 prefix 属性,不如让 <typedef><taskdef> 任务获得一个 uri 属性,该属性存储与类型关联的 XML 命名空间的 URI。

<typedef resource="org/example/tasks.properties" uri="http://example.org/tasks"/>
<my:task xmlns:my="http://example.org/tasks">
    ...
</my:task>

如上例所示,命名空间 URI 需要至少指定两次:一次作为 uri 属性的值,另一次通过使用 xmlns 属性将命名空间实际映射到来自该命名空间的元素的出现。这种映射可以在构建文件的任何级别进行。

<project name="test" xmlns:my="http://example.org/tasks">
    <typedef resource="org/example/tasks.properties" uri="http://example.org/tasks"/>
    <my:task>
        ...
    </my:task>
</project>

当然,使用命名空间前缀是可选的。因此,示例也可以如下所示:

<project name="test">
    <typedef resource="org/example/tasks.properties" uri="http://example.org/tasks"/>
    <task xmlns="http://example.org/tasks">
        ...
    </task>
</project>

在这里,命名空间被设置为 <task> 元素及其所有后代的默认命名空间。

默认命名空间

Ant 使用的默认命名空间是 antlib:org.apache.tools.ant

<typedef resource="org/example/tasks.properties" uri="antlib:org.apache.tools.ant"/>
<task>
    ...
</task>

命名空间和嵌套元素

在 Ant 1.6 中,几乎总是将嵌套在命名空间元素中的元素与父元素具有相同的命名空间。因此,如果上面的示例中的 task 允许嵌套 config 元素,则构建文件片段将如下所示:

<typedef resource="org/example/tasks.properties" uri="http://example.org/tasks"/>
<my:task xmlns:my="http://example.org/tasks">
    <my:config a="foo" b="bar"/>
    ...
</my:task>

如果元素允许或需要大量嵌套元素,则需要为每个嵌套元素使用前缀。将命名空间设置为默认值可以减少脚本的冗长性。

<typedef resource="org/example/tasks.properties" uri="http://example.org/tasks"/>
<task xmlns="http://example.org/tasks">
    <config a="foo" b="bar"/>
    ...
</task>

从 Ant 1.6.2 开始,嵌套在命名空间元素中的元素也可能位于 Ant 的默认命名空间中。这意味着现在允许以下操作:

<typedef resource="org/example/tasks.properties"
    uri="http://example.org/tasks"/>
<my:task xmlns:my="http://example.org/tasks">
    <config a="foo" b="bar"/>
    ...
</my:task>

命名空间和属性

属性仅在以下情况下用于配置它们所属的元素:

从 Ant 1.9.1 开始,可以使用两个属性命名空间 ant:ifant:unless 来允许您有条件地插入元素。

其他属性将被忽略。

这意味着两者

<my:task xmlns:my="http://example.org/tasks">
    <my:config a="foo" b="bar"/>
    ...
</my:task>

<my:task xmlns:my="http://example.org/tasks">
    <my:config my:a="foo" my:b="bar"/>
    ...
</my:task>

都会导致参数 ab 被用作参数来配置嵌套的 config 元素。

这也意味着您可以使用来自其他命名空间的属性来使用额外的元数据标记构建文件,例如 RDF 和 XML-Schema(无论这是否是一件好事)。对于来自未知命名空间的元素,情况并非如此,它们会导致错误。

混合来自不同命名空间的元素

现在是困难的部分:来自不同命名空间的元素可以在某些情况下交织在一起。这与 Ant 1.6 的 添加类型内省规则 有很大关系:Ant 类型和任务现在可以自由地接受任意命名的类型作为嵌套元素,只要具体类型实现了任务/类型期望的接口即可。最明显的例子是 <condition> 任务,它支持各种嵌套条件,所有这些条件都扩展了接口 Condition。要将自定义条件集成到 Ant 中,您现在只需 <typedef> 该条件,然后在允许嵌套条件的任何地方使用它(假设包含元素具有通用的 add(Condition)addConfigured(Condition) 方法)。

<typedef resource="org/example/conditions.properties" uri="http://example.org/conditions"/>
<condition property="prop" xmlns="http://example.org/conditions">
    <and>
        <available file="bla.txt"/>
        <my:condition a="foo"/>
    </and>
</condition>

在 Ant 1.6 中,此功能无法像我们希望的那样使用:很多代码尚未适应新的内省规则,并且像 Ant 的内置条件和选择器这样的元素在 1.6 中实际上并不是类型。预计这种情况将在 Ant 1.7 中发生变化。

命名空间和 Antlib

新的 AntLib 功能也与 Ant 1.6 中的命名空间支持紧密集成。基本上,您可以通过对命名空间 URI 使用特殊方案来“导入”Antlib:antlib 方案,它期望位于特殊 antlib.xml 文件所在的包名称。