Ant自定义task和type

ant手册:包含了ant官方提供的所有内置task和type

1. 概述

1.1 插件结构

作为现在Java构建工具中事实上的标准,Ant被设计成可以通过Java类进行扩展,而且只需要很少的Java代码,就可以编写一个新的Ant任务。

其实编写一个 Ant任务非常简单,只需要编写一个带有一个execute()方法的Java类就行了。

Package  org.cactus.ant.tasks

Public  class  SimpleTask{

  Public  void  execute(){

       System.out.println(“Simple  Task!”);


}

这个Java不需要扩展任何基类,只需要一个方法就行:execute( )。但这个方法必须是公有的(public),没有参数,而这个类必须能通过无参的构造函数实例化。这个方法可以有返回值,但是会被忽略并且产生一个警告信息。任务在执行过程中可以有System.out和System.err的输出,但是会被Ant截获并按照适当的日志级别写入相应的日志。

标签用于自定义一些任务,从而将第三方的任务集成到构建文件中。你可以在目标()之外声明,以便把这些任务定义成全局任务。不过任务的类文件必须位于其可见的classpath内。为了可以在同一个构建过程中使用编译过的任务,必须出现在编译之后。

<?xml  version=”1.0”?>

<project  name=”tasks”  default=”main”>

       <property  name=”build.dir”  location=”build”/>

       <target  name=”init”>

              <mkdir  dir=”${build.dir}”>

       </target>

       <target  name=”compile”  depends=”init”>

              <javac  srcdir=”src”  destdir=”${build.dir}”/>

       </target>

       <target  name=”simpletask”  depends=”compile”>

              <taskdef  name=”simpletask” classname=”org.example.antbook.tasks.SimpleTask” classpath=”${build.dir}”>

              <simpletask/>

      </target>

      <target  name=”clean”>

            <delete  dir=”${build.dir}”>

      </target>

      <target  name=”main”  depends=”simpletask”/>

</project>
1.2 任务的生命周期

下面是Ant任务的生命周期,构建过程从装载Ant和解析构建文件开始:

  • 当Ant 解析构建文件时,对文件中声明的每个任务,通过不带参数的构造函数建立一个恰当的Task的子类的实例。
  • Ant传递一些信息给任务,包括任务所在的工程和目标、以及其他一些次要细节,例如它出现在构建文件中的行数。
  • Ant调用Task类的init( )函数。大多数任务都不重写这个函数。
  • Ant按照自己的方式,一次执行目标(就是依据depends属性指定的顺序)。
  • 一个目标中的任务得以逐个执行。对于每一个任务,Ant先根据构建文件的属性和元素的值对其进行配置,然后调用其execute( )函数。
    Ant内部使用TaskAdapter,一个派生自org.apache.tools.ant.Task的任务适配器来处理那些不扩展Task类的任务。适配器中有个Object实例,并会调用任务对象的execute()方法。

2.Ant API

现在我们就来了解一下Ant中主要的API,作为开发者的我们需要经常和它们打交道。

2.1 Task

抽象类org.apache.tools.ant.Task是Ant任务最常用的基类,是Ant构建过程中的主要工作单元。Task类通过保护类型成员变量project,提供对project对象的访问。还有log方法则可以将输出传给Ant进程。Task类中主要是init()和execute()方法需要重载,而log()方法(log(String msg, int msgLevel)和log(String msg))主要是用于调用,它是调用project对象的log函数。其中有五个日志级别(按优先级排列):

  • MSG_ERR
  • MSG_WARN
  • MSG_INFO
  • MSG_VERBOSE
  • MSG_DEBUG
2.2 Project
  • String getProperty(String name):返回某个Ant特性的值,如果不存在这个特性,则返回null。
  • Void setNewProperty(String name, String value):给一个特性赋值,但要记住Ant中的特性是不可变更的。所以这个方法在这个特性存在的情况下,就什么都不作。
  • File getBaseDir():这个函数返回工程的基准目录(base directory)。它对解析相对路径非常有用,虽然在实际中用的并不多,因为Ant的文件和路径已经具有了自动展开的功能。
  • File resolveFile(String filename):这个函数根据文件名返回带有绝对路径的File对象。假如文件名是个相对值,则它会根据工程的基准目录进行解析。
2.3 Path
  • String toString():返回经过完全解析的、且平台相关的完整路径信息。
  • static String[] translatePath(Project project , String path):这个工具函数根据某个路径分解得到路径元素数组,那个路径包含着由冒号或者分号分隔开来的路径元素。
  • int size():返回Path实例中路径元素的数量。
  • String[] list():返回Path实例中路径元素数组。
2.4 Fileset
2.5 DirectoryScanner
2.6 EnumeratedAttribute
2.7 FileUtils

3 任务获取数据的机制

在构建文件中,任务是由XML元素的形式来声明的,它包含了属性、子元素以及文本。在Ant中,任务可以通过多种形式的途径获取领域相关(domain-specific)的信息。

在构建文件执行过程中,Ant为用到的任务所对应的类建立实例,并将属性和子元素的消息传递给它。利用Java的内省(introspection)机制,Ant寻找特定命名的函数,并用构建文件的数据调用这些函数。在数据传送过程中,Ant任务和其它成分(例如datatype、target以及project)都是平等的,都对应一个对象。

3.1 属性

XML属性其实就是一个名值对。假如你现在有一个仅有一个属性的任务:

private  String  value;

public  void  setValue  (String  value) {

      phis.value = value;

}

这和JavaBean的命名风格很相似,每个特性都对应一个读写器(setter/getter)。属性的内省机制支持Java的基本类型和包装类型,而类型之间的转换出现错误的话,将抛出NumberFormatException异常,构建过程将停止。

通过实现一个以java.io.File参数的设值函数,Ant提供了对文件或者目录属性内置的支持,它可以将构建文件中的相对目录解析为绝对路径。

<mytask  destdir =”output”/>

对应的任务就实现了setDestDir函数:

private  File  destDir;

public  void  setDestDir(File  destDir) {

        This.destDir  =  destDir;

}
3.2 支持嵌套元素

当遇到嵌套元素时,Ant会在任务中寻找特定命名的函数,然后调用它。下面是三种特定的Ant调用特定命名函数的情况:

  • Ant可以利用默认构造函数创建对象,且无需前期配置:public void addElementName(Object obj)
  • Ant可以利用默认的构造函数创建对象,但需要前期配置:public void addConfigredElementName(ObjectTyep obj)
  • 需要你的任务自行创建对象:public ObjectType createElementName()

建议对嵌套的元素使用addXXX(或者addConfiguredXXX)函数,而不是createXXX函数,因为前者允许类型的多态。

3.3 支持Datatypes

嵌套的datatype隐式地支持refid引用,所以无需在任务的代码中显性地增加这个支持。如果试图导入对定制datatype的引用,就必须用相同的类加载器(classloader)载入任务和datatype。当将JAR文件放入Ant的lib目录中时,上述操作就会自动进行。如果你在和声明中指定了一个类加载器,那么就必须在所有的声明中,将loaderref属性设置为引用相同的类加载器。

4 Ant Task例子

在这里自定义的任务都从org.apache.tools.ant.Task扩展,因为这样可以访问Ant的内部API。Task类提供了:

  • 所属目标的入口
  • 当前工程的入口
  • 日志工具

不扩展Ant的Task类,通过自定义一些同名函数,依然可以访问工程实例和日志工具。这样的好处在于避免了你的泪依赖于Ant,同时又能保持自己的继承层次体系。一旦使用了Ant内部的API,就建立了与Ant的依赖关系。如果你为了保持继承层次体系,可以将其他的Java类包装到Task子类中去,Task子类就变成了一个包装类(wrapper),这样既可以修改被包装的代码,又可以保持任务与构建文件的访问借口不变。

4.1 添加属性

下面是一个从Task扩展的基本的Ant任务,同时还带有一个可选的属性:

package org.cactus.ant.task

import org.apache.tools.ant.Task;

public class MessageTask extends Task{

       private String message = “ ”;

       public void setMessage(String message){

              this.message = message;

       }

       public void execute(){

            log(message);

       }

}

从而在构建文件中就可以这样使用了:

<target name=”messagetask” depends=”compile”>

       <taskdef name=”message” classname=”org.cactus.ant.task.MessageTask” Classpath=”${build.dir}”>

              <property name = “the.messagevalue=”blue scooter”/>

              <message message=”${the.message}”/>

</target>
4.2 处理元素文本

addText函数将元素文本传递给它所属的对象,通常是任务自身,即将构建文件中的文本传递给自定义的类。在传递过程中对文本不做任何的变动,也不对特性引用进行展开。我们现在对上面的例子进行一些修改:

package org.cactus.ant.task

import org.apache.tools.ant.Task;

public class MessageTask2 extends Task{

       private String message = "";

       public void addText(String message){

              this.message = message;
       }

      public void execute(){

             log(message);

      }

}

而构建文件中的片断如下:

<property name=”another.message” value=”light up ahead”/>

<message2>${another.message}</message2>

输出结果是:

[message2] ${another.message}

如果你想解析特性的引用,只需要调用一个Project函数就行:

public void execute(){

      log(getProject().replaceProperties(message));

}

5 构建任务库

构建可重用任务库,从而使得在编写构建文件时,能更加方便地使用已有的任务。在库中包含一个特性文件,用它可以将构建文件中的任务名映射到实际实现的Java类名:

message2=org.cactus.ant.task.MessageTask

然后再将这个库打成一个jar包,这样一个新的自定义的Ant任务就可以使用了。

猜你喜欢

转载自blog.csdn.net/mbshqqb/article/details/79031116