Java的Build工具——Ant应用指南

Java的Build工具——Ant应用指南

Ant是一种基于Java的Build工具。理论上来说,它有些类似于C中的make,但比make优越。现在存在的大多数Build工具,如make、gnumake、nmake、jam等都存在这样或那样的不足,比如依赖于特定的平台、配置文件过于复杂或者对格式无法检查而容易出错等。与这些工具相比较,Ant的两个特性决定了它是一款优秀的Build工具:

1. 基于Java的实现。具有良好的跨平台性,同时可以通过增加新的Java类来扩展Ant的功能,而无需去了解不同平台上不同的脚本语言。

2.基于XML的配置文件。Ant以XML树来描述Target/Task的关系,文件结构清晰、易读易写,并且利用XML对格式的控制来避免由于配置文件的错误造成的Build操作失败。 

安装与配置



Ant的安装非常简单,把从网上下载的jakarta-ant-1.5.1-bin.zip解开到一个目录下即可(以下假定安装在目录D:/jakarta-ant-1.5.1)。接下来需要进行环境变量配置:

      
      SET ANT_HOME=D:/jakarta-ant-1.5.1 //注意是Ant的安装目录,不是bin子目录SET PATH=%PATH%;%ANT_HOME%/bin;



在配置环境变量之前,请确认已经正确设置了JAVA_HOME系统变量。输入ant命令,看到如下输出说明已成功安装了Ant工具:

      
      Buildfile: build.xml does not exist!Build failed



提示信息表明在当前目录不存在build.xml配置文件,但这本身已经说明Ant成功运行了。

快速入门



下面用一个最简单也是最经典的例子-HelloWorld来感受一下Ant吧。

      
      //HelloWorld.javapackage com.sharetop.antdemo;public class HelloWorld  { public static void main( String args[] ) {  System.out.println("Hello world. "); }}



要让Ant编译这个文件,首先需要编写一个Build配置文件。在一般情况下,这个文件被命名为build.xml。

      
      <?xml version="1.0" encoding="UTF-8" ?><project name="HelloWorld" default="run" basedir="." ><property name="src" value="src"/><property name="dest" value="classes"/><property name="hello_jar" value="hello.jar" /><target name="init"><mkdir dir="${dest}"/></target><target name="compile" depends="init"><javac srcdir="${src}" destdir="${dest}"/></target><target name="build" depends="compile"><jar jarfile="${hello_jar}" basedir="${dest}"/></target><target name="run" depends="build"><java classname="com.sharetop.antdemo.HelloWorld" classpath="${hello_jar}"/></target></project>



来看一下这个文件的内容,它描述了以下信息:工程的名字为HelloWorld,工程有四个target,分别是init、compil、build和run,缺省是run。compile只有一个任务javac,源文件位于src目录下,输出的类文件要放在classes目录下。build的任务是jar,生成的jar文件为hello.jar,它打包时以classes为根目录。而run则是执行这个HelloWorld类,用hello.jar作为classpath。这四个target之间有一个依赖关系,这种关系用depends来指定。即如果Target A依赖于Target B,那么在执行Target A之前会首先执行Target B。所以从下面运行缺省Target(run)的输出看,这四个Target的执行顺序是:init→compile→build→run。文件目录结构如图1所示。HelloWorld.java文件在src/com/sharetop/antdemo子目录下。 



图1 ant_demo应用的目录结构



在命令行输入命令:ant,然后运行,可以看到如下输出:

如果配置文件名不是build.xml,比如是build_front.xml,那么,可以使用-buildfile命令参数指定:

      
      G:/myDoc/ant_demo>ant -buildfile build_front.xml



也可以单独执行指定的某个target,比如,只编译不打包执行,可以使用下面输入命令即可:

      
      G:/myDoc/ant_demo>ant compile



在相应的目录下会找到编译出的HelloWorld.class文件。

再看看上面的build.xml配置文件,文件开头定义了3个属性,分别指定了源文件输出路径、类文件输出路径和生成的Jar文件名,后面对这些路径的引用都通过一个${property name}来引用。所以,要注意这样一个原则“目录的定义与目录的引用应该分开”。

基本应用



建立工程的目录

一般要根据工程的实际情况来建立工程的目录结构。但是,有一些比较通用的组织形式可供参考,比如所有的jakarta项目都使用类似的目录结构。下面让我们来看一下这种目录结构的特点。

表1



 

目录文件
bin公共的二进制文件,以及运行脚本
build临时创建的文件,如类文件等
dist目标输出文件,如生成Jar文件等。
doc/javadocs文档。
lib需要导出的Java包
src源文件



对于一个简单的工程,一般包括表1的几个目录。其中bin、lib、doc和src目录需要在CVS的控制之下。当然在这样的目录结构上,也可以做一些调整,例如,可以建立一个extra目录来放置需要发布的Jar文件、Inf文件及图像文件等。同样,如果开发Web应用可以建立一个Web目录放置JSP、HTML等文件。

如果我们开发的是一个比较复杂的项目,包括多个子项目,并且各个子项目是由不同的开发人员来完成的,那么要如何来设计它的目录结构?首先有一点是需要确定的,不同的子项目应该拥有不同的Build文件,并且整个项目也应该有一个总的Build文件。可以通过Ant任务或是AntCall任务调用子项目的Build文件,如下例:

      
      <target name="core" depends="init"><ant dir="components" target="core"/><ant dir="waf/src" target="core"/><ant dir="apps" target="core"/>     </target>


在各个子项目的耦合不是非常紧密的情况下,各个子项目应该有各自独立的目录结构,也就是说它们可以有自己的src、doc、build、dist等目录及自己的build.xml文件,但是可以共享lib和bin目录。而对于那些耦合紧密的子项目,则推荐使用同一个src目录,但是不同的子项目有不同的子目录,各个子项目的build.xml文件可以放在根目录下,也可以移到各个子项目的目录下。

编写Build文件

要用好Ant工具,关键是要编写一个build.xml文件。要编写出一个结构良好、灵活可扩展的Build文件,有两个问题要考虑,一是了解Build文件的基本结构,二是了解Ant定义的大量任务。

Ant的Build文件是一个标准的XML文件,它包含一个根节点Project,每个Project定义了至少一个或多个Target,每个Target又是一系列Task的集合。它们之间的关系如图2所示。



 

图2 build.xml文件的结构



每个Task是一段可被执行的代码,比如,前例中的javac、jar就是两个最常用的Task。Ant定义了大量的核心Task,我们要考虑的第二个问题正是如何去掌握这大量的Task。其实唯一的方法就是边学习边实践,这方面最好的参考就是官方的Ant使用手册。

外部文件的使用

使用外部的Property文件可以保存一些预设置的公共属性变量。这些属性可以在多个不同的Build文件中使用。

可以将一个外部的XML文件导入Build文件中,这样多个项目的开发者可以通过引用来共享一些代码,同样,这也有助于Build文件的重用,示例代码如下所示:

      
      <!DOCTYPE project [<!ENTITY share-variable SYSTEM "file:../share-variable.xml"><!ENTITY build-share SYSTEM "file:../build-share.xml">]><project name="main" default="complie" basedir=".">&share-variable;&build-share;... ...


在J2EE项目中的应用



只要掌握了Ant的使用方法,在J2EE项目中的应用与在其它项目中的应用并没有太大的不同,但是仍有几点是需要注意的。

一是要清楚War和Jar文件的目录结构,主要是War的配置文件web.xml文件的位置和EJB的配置文件(ejb-jar.xml和weblogic-ejb-jar.xml等)的位置,在调用Jar任务打包文件时一定要记得把它们也包含进来。一般在编译之前就要注意把这些需打包的文件拷入相应目录下。二是在J2EE项目中可能会涉及到一些特殊的任务,比如在Weblogic中会调用ejbc预编译EJB的代码存根,或者需要在Ant中同时发布Jar到相应的服务器中等。可以用两种途径实现这些任务,一是扩展Ant任务实现这些任务,二是直接用Java任务来执行这些命令。下面是打包、发布一个EJB的build.xml配置文件片断,代码如下:

      
      <target name="deploy_HelloEJB" depends="compile"><delete dir="${temp}/ejb_make"/>  <!-- 首先删除临时目录 --><delete file="${temp}/helloEJB.jar"/><!-- 删除WebLogic域中老版本的EJB --><delete file="${weblogic.deploy.dest}/helloEJB.jar"/> <!-- 创建META-INF目录,放置ejb-jar.xml和weblogic-ejb-jar.xml --><mkdir dir="${temp}/ejb_make/META-INF"/> <!-- 拷贝ejb-jar.xml和weblogic-ejb-jar.xml 到临时目录--><copy todir="${temp}/ejb_make/META-INF"><fileset dir="etc/baseinfo"><include name="*.xml"/></fileset></copy><!-- 拷贝所有的helloEJB类到临时目录 --><copy todir="${temp}/ejb_make/"><fileset dir="${dest.classes}/">  <!-- dest.classes是输出的类文件目录 --><include name="${dest.classes}/helloEJB/**"/></fileset></copy><!-- 将所有这些文件打包成helloEJB.jar --><jar jarfile="${temp}/helloEJB.jar" basedir="${temp}/ejb_make"/><!-- 进行weblogic.ejbc编译 --><java classpath="${wl_cp}" classname="weblogic.ejbc" fork="yes" ><classpath><fileset dir="lib"><include name="*.jar" /></fileset></classpath><arg value="${temp}/helloEJB.jar" /><arg value="${temp}/helloEJB_deploy.jar" /></java><!-- 拷贝/发布到WebLogic的{DOMAIN}/applications目录 --><copy file="${temp}/helloEJB_deploy.jar" todir="${weblogic.deploy.dest}"/> </target>


用Ant配合JUnit实现单元测试



Ant 提供了JUnit任务,可以执行单元测试代码。如何使用JUnit,以及如何编写测试用例(TestCase),感兴趣的读者可以参阅JUnit的相关文档。在Ant中使用JUnit的方法非常简单,首先需要把junit.jar拷入ANT_HOME/lib下,确认在这个目录下有optional.jar,因为JUnit是Ant的扩展任务,需要引用这个扩展包。然后就是在Build文件中加入JUnit的任务,代码如下:

      
      <target name="run" depends="client"><junit printsummary="yes" fork="yes" haltonfailure="yes"><classpath><pathelement location="client.jar" /></classpath><formatter type="plain" /><test name="com.sharetop.antdemo.HelloWorldTest" /></junit></target>


高级话题



为Ant开发扩展任务

为Ant实现扩展任务其实是非常容易的,只需按照以下几个步骤即可:

1. 创建一个Java类继承org.apache.tools.ant.Task类;

2. 对每个属性实现set方法。Ant会根据需要自动完成类型转换;

3. 如果扩展的任务需要嵌套其它的Task,那么这个Java类必需实现接口org.apache.tools.ant.TaskContainer;

4. 如果扩展的任务要支持Text,需要增加一个方法void addText(String);

5. 对每个嵌套的元素,实现create、add 或 addConfigured 方法;

6. 实现public void execute方法;

7. 在build.xml文件中使用 <taskdef>来引用自定义的Task。

下面以一个简单的例子来说明如何为Ant增加一个hello任务,它可以连续打印多条信息,打印的次数由属性count指定,而打印的内容则由它内嵌的一个helloinfo任务的message属性指定,看上去这非常类似JSP中自定义标签的一些概念,实现代码如下:

      
      //HelloInfoTask.javapackage com.sharetop.antdemo;import org.apache.tools.ant.*;public class HelloInfoTask { private String msg; public void execute() throws BuildException {  System.out.println(msg); } public void setMessage(String msg) {  this.msg = msg; }}


下面是外部Task类的代码:

      
      //HelloTask.javapackage com.sharetop.antdemo;import org.apache.tools.ant.*;public class HelloTask extends Task implements org.apache.tools.ant.TaskContainer { private Task info; private int count; public void execute() throws BuildException {  for(int i=0;i<count;i++)  info.execute(); } public void setCount(int c){  this.count=c; } public void addTask(Task t){  this.info=t; }}


实现了这两个Task,在build.xml文件中定义它的task name,就可以在Target中执行它了。如果你不想使用 <taskdef>标签来定义Task,也可以通过修改default.properties文件来实现引入新Task,这个文件位于org.apache.tools.ant.taskdefs 包里。下例是一个使用 标签来引入新Task的Build文件部分:

      
      <target name="hello" depends="client"><taskdef name="hello" classname="com.sharetop.antdemo.HelloTask" classpath="client.jar"/><taskdef name="helloinfo"classname="com.sharetop.antdemo.HelloInfoTask" classpath="client.jar"/><hello count="3" ><helloinfo message="hello world" /></hello></target>

在自己的程序中调用Ant

Ant的任务其实就是一段功能代码。Ant内置的大量任务对于我们开发Java应用具有非常大的意义,为什么我们不能直接使用呢?

因为尽管在程序中调用Ant的任务并不复杂,而且我们知道Ant的任务其实都是一些Java类,调用的方法无非就是执行这些类而已,不过在执行之前需要对它做一些初始化的工作,所以我们需要引用一个Task类的子类来实现这个功能,比如如下代码:

      
      package com.sharetop.antdemo;import org.apache.tools.ant.*;import org.apache.tools.ant.taskdefs.*;import java.io.File;public class RunAntTask { public RunAntTask() { } public static void main(String args[]){  AntJAR j = new AntJAR();  j.setBasedir(new File("./classes"));  j.setJarfile(new File("aaa.jar"));  j.execute(); }}final class AntJAR extends Jar { public AntJAR() {  project = new Project();  project.init();  taskType = "jar";  taskName = "jar"; }}


注意AntJAR类的构造方法,先创建了Project并初始化它,这是直接调用Task的必需条件。

如果要在自己的程序中执行Ant,需要了解的是Ant定义的几个BuildEvent,它包括:

◆ Build started

◆ Build finished

◆ Target started

◆ Target finished

◆ Task started

◆ Task finished

◆ Message logged

我们需要做的是实现BuildListener接口来处理各种事件,而执行Ant的方法与上面给的例子非常类似,以实际开发的AntBuilder软件的部分代码为例:

      
      public void buildTarget(String targetName,String buildFileName) { try {  Project p = new Project();  p.init();  File f = new File(buildFileName);  p.setUserProperty("ant.file",f.getAbsolutePath());  ProjectHelper.configureProject(p,f);  p.addBuildListener(this);  if( targetName==null )  p.executeTarget(p.getDefaultTarget());  else  p.executeTarget(targetName); } catch (Exception ex) {  jTextArea1.append(ex.getMessage()); }}


创建Project并初始化,设置它的配置文件(build.xml),执行它缺省的或指定的Target,然后在实现了BuildListenser接口的监听器类中对你感兴趣的事件作处理,代码如下:

      
      public void buildStarted(BuildEvent event){ /* nothing*/ }  public void buildFinished(BuildEvent event) { /* nothing*/ }  public void targetStarted(BuildEvent event) {    this.jTextArea1.append(event.getTarget().getName()+": /n/r");  }  public void targetFinished(BuildEvent event) {/* nothing*/ }  public void taskStarted(BuildEvent event) {/* nothing*/ }  public void taskFinished(BuildEvent event) { /* nothing*/ }  public void messageLogged(BuildEvent event) {   int prior = event.getPriority();   switch(prior){    case Project.MSG_ERR :     this.jTextArea1.append("["+event.getTask().getTaskName()+"]Err:"   +event.getMessage());     break;    case Project.MSG_INFO:  this.jTextArea1.append("["+event.getTask().getTaskName()+"]"+event.getMessage ());     break;    case Project.MSG_WARN:    this.jTextArea1.append("["+event.getTask().getTaskName()+"]"   +event.getMessage());     break;    case Project.MSG_VERBOSE:     this.jTextArea1.append(event.getMessage());     break;   }  }


Build.xml文件的写法每个公司都有不同,这里没有太大的参考价值,所以略去。



Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=623757

 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值