隨筆-22  評(píng)論-6  文章-17  trackbacks-0

          本文以最新發(fā)布的Ant 1.5.1為例,介紹這款優(yōu)秀的Build工具的安裝配置、基本應(yīng)用和一些高級(jí)話題。最新的Ant下載地址是 http://jakarta.apache.org/ant/

          Ant是一種基于Java的Build工具。理論上來說,它有些類似于C中的make,但比make優(yōu)越。現(xiàn)在存在的大多數(shù)Build工具,如make、gnumake、nmake、jam等都存在這樣或那樣的不足,比如依賴于特定的平臺(tái)、配置文件過于復(fù)雜或者對(duì)格式無法檢查而容易出錯(cuò)等。與這些工具相比較,Ant的兩個(gè)特性決定了它是一款優(yōu)秀的Build工具:

          1. 基于Java的實(shí)現(xiàn)。具有良好的跨平臺(tái)性,同時(shí)可以通過增加新的Java類來擴(kuò)展Ant的功能,而無需去了解不同平臺(tái)上不同的腳本語言。

          2.基于XML的配置文件。Ant以XML樹來描述Target/Task的關(guān)系,文件結(jié)構(gòu)清晰、易讀易寫,并且利用XML對(duì)格式的控制來避免由于配置文件的錯(cuò)誤造成的Build操作失敗。

          安裝與配置

          Ant的安裝非常簡單,把從網(wǎng)上下載的jakarta-ant-1.5.1-bin.zip解開到一個(gè)目錄下即可(以下假定安裝在目錄D:\jakarta-ant-1.5.1)。接下來需要進(jìn)行環(huán)境變量配置:

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


          在配置環(huán)境變量之前,請(qǐng)確認(rèn)已經(jīng)正確設(shè)置了JAVA_HOME系統(tǒng)變量。輸入ant命令,看到如下輸出說明已成功安裝了Ant工具:

          Buildfile: build.xml does not exist!
          Build failed


          提示信息表明在當(dāng)前目錄不存在build.xml配置文件,但這本身已經(jīng)說明Ant成功運(yùn)行了。

          快速入門

          下面用一個(gè)最簡單也是最經(jīng)典的例子-HelloWorld來感受一下Ant吧。

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


          要讓Ant編譯這個(gè)文件,首先需要編寫一個(gè)Build配置文件。在一般情況下,這個(gè)文件被命名為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>


          來看一下這個(gè)文件的內(nèi)容,它描述了以下信息:工程的名字為HelloWorld,工程有四個(gè)target,分別是init、compil、build和run,缺省是run。compile只有一個(gè)任務(wù)javac,源文件位于src目錄下,輸出的類文件要放在classes目錄下。build的任務(wù)是jar,生成的jar文件為hello.jar,它打包時(shí)以classes為根目錄。而run則是執(zhí)行這個(gè)HelloWorld類,用hello.jar作為classpath。這四個(gè)target之間有一個(gè)依賴關(guān)系,這種關(guān)系用depends來指定。即如果Target A依賴于Target B,那么在執(zhí)行Target A之前會(huì)首先執(zhí)行Target B。所以從下面運(yùn)行缺省Target(run)的輸出看,這四個(gè)Target的執(zhí)行順序是:init→compile→build→run。文件目錄結(jié)構(gòu)如圖1所示。HelloWorld.java文件在src\com\sharetop\antdemo子目錄下。


          圖1 ant_demo應(yīng)用的目錄結(jié)構(gòu)


          在命令行輸入命令:ant,然后運(yùn)行,可以看到如下輸出:

          如果配置文件名不是build.xml,比如是build_front.xml,那么,可以使用-buildfile命令參數(shù)指定:

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


          也可以單獨(dú)執(zhí)行指定的某個(gè)target,比如,只編譯不打包執(zhí)行,可以使用下面輸入命令即可:

          G:\myDoc\ant_demo>ant compile

          在相應(yīng)的目錄下會(huì)找到編譯出的HelloWorld.class文件。

          再看看上面的build.xml配置文件,文件開頭定義了3個(gè)屬性,分別指定了源文件輸出路徑、類文件輸出路徑和生成的Jar文件名,后面對(duì)這些路徑的引用都通過一個(gè)${property name}來引用。所以,要注意這樣一個(gè)原則“目錄的定義與目錄的引用應(yīng)該分開”。
          基本應(yīng)用

          建立工程的目錄

          一般要根據(jù)工程的實(shí)際情況來建立工程的目錄結(jié)構(gòu)。但是,有一些比較通用的組織形式可供參考,比如所有的jakarta項(xiàng)目都使用類似的目錄結(jié)構(gòu)。下面讓我們來看一下這種目錄結(jié)構(gòu)的特點(diǎn)。

          表1

          目錄 文件
          bin 公共的二進(jìn)制文件,以及運(yùn)行腳本
          build 臨時(shí)創(chuàng)建的文件,如類文件等
          dist 目標(biāo)輸出文件,如生成Jar文件等。
          doc/javadocs 文檔。
          lib 需要導(dǎo)出的Java包
          src 源文件

          對(duì)于一個(gè)簡單的工程,一般包括表1的幾個(gè)目錄。其中bin、lib、doc和src目錄需要在CVS的控制之下。當(dāng)然在這樣的目錄結(jié)構(gòu)上,也可以做一些調(diào)整,例如,可以建立一個(gè)extra目錄來放置需要發(fā)布的Jar文件、Inf文件及圖像文件等。同樣,如果開發(fā)Web應(yīng)用可以建立一個(gè)Web目錄放置JSP、HTML等文件。

          如果我們開發(fā)的是一個(gè)比較復(fù)雜的項(xiàng)目,包括多個(gè)子項(xiàng)目,并且各個(gè)子項(xiàng)目是由不同的開發(fā)人員來完成的,那么要如何來設(shè)計(jì)它的目錄結(jié)構(gòu)?首先有一點(diǎn)是需要確定的,不同的子項(xiàng)目應(yīng)該擁有不同的Build文件,并且整個(gè)項(xiàng)目也應(yīng)該有一個(gè)總的Build文件。可以通過Ant任務(wù)或是AntCall任務(wù)調(diào)用子項(xiàng)目的Build文件,如下例:

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


          在各個(gè)子項(xiàng)目的耦合不是非常緊密的情況下,各個(gè)子項(xiàng)目應(yīng)該有各自獨(dú)立的目錄結(jié)構(gòu),也就是說它們可以有自己的src、doc、build、dist等目錄及自己的build.xml文件,但是可以共享lib和bin目錄。而對(duì)于那些耦合緊密的子項(xiàng)目,則推薦使用同一個(gè)src目錄,但是不同的子項(xiàng)目有不同的子目錄,各個(gè)子項(xiàng)目的build.xml文件可以放在根目錄下,也可以移到各個(gè)子項(xiàng)目的目錄下。

          編寫B(tài)uild文件

          要用好Ant工具,關(guān)鍵是要編寫一個(gè)build.xml文件。要編寫出一個(gè)結(jié)構(gòu)良好、靈活可擴(kuò)展的Build文件,有兩個(gè)問題要考慮,一是了解Build文件的基本結(jié)構(gòu),二是了解Ant定義的大量任務(wù)。

          Ant的Build文件是一個(gè)標(biāo)準(zhǔn)的XML文件,它包含一個(gè)根節(jié)點(diǎn)Project,每個(gè)Project定義了至少一個(gè)或多個(gè)Target,每個(gè)Target又是一系列Task的集合。它們之間的關(guān)系如圖2所示。


          圖2 build.xml文件的結(jié)構(gòu)


          每個(gè)Task是一段可被執(zhí)行的代碼,比如,前例中的javac、jar就是兩個(gè)最常用的Task。Ant定義了大量的核心Task,我們要考慮的第二個(gè)問題正是如何去掌握這大量的Task。其實(shí)唯一的方法就是邊學(xué)習(xí)邊實(shí)踐,這方面最好的參考就是官方的Ant使用手冊(cè)。

          外部文件的使用

          使用外部的Property文件可以保存一些預(yù)設(shè)置的公共屬性變量。這些屬性可以在多個(gè)不同的Build文件中使用。

          可以將一個(gè)外部的XML文件導(dǎo)入Build文件中,這樣多個(gè)項(xiàng)目的開發(fā)者可以通過引用來共享一些代碼,同樣,這也有助于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項(xiàng)目中的應(yīng)用

          只要掌握了Ant的使用方法,在J2EE項(xiàng)目中的應(yīng)用與在其它項(xiàng)目中的應(yīng)用并沒有太大的不同,但是仍有幾點(diǎn)是需要注意的。

          一是要清楚War和Jar文件的目錄結(jié)構(gòu),主要是War的配置文件web.xml文件的位置和EJB的配置文件(ejb-jar.xml和weblogic-ejb-jar.xml等)的位置,在調(diào)用Jar任務(wù)打包文件時(shí)一定要記得把它們也包含進(jìn)來。一般在編譯之前就要注意把這些需打包的文件拷入相應(yīng)目錄下。二是在J2EE項(xiàng)目中可能會(huì)涉及到一些特殊的任務(wù),比如在Weblogic中會(huì)調(diào)用ejbc預(yù)編譯EJB的代碼存根,或者需要在Ant中同時(shí)發(fā)布Jar到相應(yīng)的服務(wù)器中等。可以用兩種途徑實(shí)現(xiàn)這些任務(wù),一是擴(kuò)展Ant任務(wù)實(shí)現(xiàn)這些任務(wù),二是直接用Java任務(wù)來執(zhí)行這些命令。下面是打包、發(fā)布一個(gè)EJB的build.xml配置文件片斷,代碼如下:

          <target name="deploy_HelloEJB" depends="compile">
          <delete dir="${temp}/ejb_make"/> <!-- 首先刪除臨時(shí)目錄 -->
          <delete file="${temp}/helloEJB.jar"/>
          <!-- 刪除WebLogic域中老版本的EJB -->
          <delete file="${weblogic.deploy.dest}/helloEJB.jar"/>
          <!-- 創(chuàng)建META-INF目錄,放置ejb-jar.xml和weblogic-ejb-jar.xml -->
          <mkdir dir="${temp}/ejb_make/META-INF"/>
          <!-- 拷貝ejb-jar.xml和weblogic-ejb-jar.xml 到臨時(shí)目錄-->
          <copy todir="${temp}/ejb_make/META-INF">
          <fileset dir="etc/baseinfo">
          <include name="*.xml"/>
          </fileset>
          </copy>
          <!-- 拷貝所有的helloEJB類到臨時(shí)目錄 -->
          <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"/>
          <!-- 進(jìn)行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>
          <!-- 拷貝/發(fā)布到WebLogic的{DOMAIN}\applications目錄 -->
          <copy file="${temp}/helloEJB_deploy.jar" todir="${weblogic.deploy.dest}"/>
          </target>


          用Ant配合JUnit實(shí)現(xiàn)單元測(cè)試

          Ant 提供了JUnit任務(wù),可以執(zhí)行單元測(cè)試代碼。如何使用JUnit,以及如何編寫測(cè)試用例(TestCase),感興趣的讀者可以參閱JUnit的相關(guān)文檔。在Ant中使用JUnit的方法非常簡單,首先需要把junit.jar拷入ANT_HOME\lib下,確認(rèn)在這個(gè)目錄下有optional.jar,因?yàn)镴Unit是Ant的擴(kuò)展任務(wù),需要引用這個(gè)擴(kuò)展包。然后就是在Build文件中加入JUnit的任務(wù),代碼如下:

          <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>


          高級(jí)話題

          為Ant開發(fā)擴(kuò)展任務(wù)

          為Ant實(shí)現(xiàn)擴(kuò)展任務(wù)其實(shí)是非常容易的,只需按照以下幾個(gè)步驟即可:

          1. 創(chuàng)建一個(gè)Java類繼承org.apache.tools.ant.Task類;

          2. 對(duì)每個(gè)屬性實(shí)現(xiàn)set方法。Ant會(huì)根據(jù)需要自動(dòng)完成類型轉(zhuǎn)換;

          3. 如果擴(kuò)展的任務(wù)需要嵌套其它的Task,那么這個(gè)Java類必需實(shí)現(xiàn)接口org.apache.tools.ant.TaskContainer;

          4. 如果擴(kuò)展的任務(wù)要支持Text,需要增加一個(gè)方法void addText(String);

          5. 對(duì)每個(gè)嵌套的元素,實(shí)現(xiàn)create、add 或 addConfigured 方法;

          6. 實(shí)現(xiàn)public void execute方法;

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

          下面以一個(gè)簡單的例子來說明如何為Ant增加一個(gè)hello任務(wù),它可以連續(xù)打印多條信息,打印的次數(shù)由屬性count指定,而打印的內(nèi)容則由它內(nèi)嵌的一個(gè)helloinfo任務(wù)的message屬性指定,看上去這非常類似JSP中自定義標(biāo)簽的一些概念,實(shí)現(xiàn)代碼如下:

          //HelloInfoTask.java
          package 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.java
          package 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;
          }
          }


          實(shí)現(xiàn)了這兩個(gè)Task,在build.xml文件中定義它的task name,就可以在Target中執(zhí)行它了。如果你不想使用 <taskdef> 標(biāo)簽來定義Task,也可以通過修改default.properties文件來實(shí)現(xiàn)引入新Task,這個(gè)文件位于org.apache.tools.ant.taskdefs 包里。下例是一個(gè)使用 標(biāo)簽來引入新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>
          在自己的程序中調(diào)用Ant

          Ant的任務(wù)其實(shí)就是一段功能代碼。Ant內(nèi)置的大量任務(wù)對(duì)于我們開發(fā)Java應(yīng)用具有非常大的意義,為什么我們不能直接使用呢?

          因?yàn)楸M管在程序中調(diào)用Ant的任務(wù)并不復(fù)雜,而且我們知道Ant的任務(wù)其實(shí)都是一些Java類,調(diào)用的方法無非就是執(zhí)行這些類而已,不過在執(zhí)行之前需要對(duì)它做一些初始化的工作,所以我們需要引用一個(gè)Task類的子類來實(shí)現(xiàn)這個(gè)功能,比如如下代碼:

          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類的構(gòu)造方法,先創(chuàng)建了Project并初始化它,這是直接調(diào)用Task的必需條件。

          如果要在自己的程序中執(zhí)行Ant,需要了解的是Ant定義的幾個(gè)BuildEvent,它包括:

          ◆ Build started

          ◆ Build finished

          ◆ Target started

          ◆ Target finished

          ◆ Task started

          ◆ Task finished

          ◆ Message logged

          我們需要做的是實(shí)現(xiàn)BuildListener接口來處理各種事件,而執(zhí)行Ant的方法與上面給的例子非常類似,以實(shí)際開發(fā)的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());
          }
          }


          創(chuàng)建Project并初始化,設(shè)置它的配置文件(build.xml),執(zhí)行它缺省的或指定的Target,然后在實(shí)現(xiàn)了BuildListenser接口的監(jiān)聽器類中對(duì)你感興趣的事件作處理,代碼如下:

          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文件的寫法每個(gè)公司都有不同,這里沒有太大的參考價(jià)值,所以略去。(全文完)
          posted on 2005-12-07 14:29 surffish 閱讀(1650) 評(píng)論(0)  編輯  收藏

          只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 乐山市| 雅江县| 辽阳市| 济阳县| 海城市| 裕民县| 敦煌市| 哈巴河县| 安图县| 平阴县| 莫力| 乌兰浩特市| 长汀县| 陈巴尔虎旗| 嘉鱼县| 望江县| 德惠市| 龙门县| 苏州市| 曲麻莱县| 阳谷县| 浑源县| 南召县| 莲花县| 麟游县| 汾阳市| 六安市| 广东省| 拜泉县| 阿拉善盟| 广饶县| 广昌县| 西峡县| 瑞昌市| 原阳县| 罗源县| 方山县| 普定县| 中牟县| 肥东县| 江源县|