本文檔提供逐步教學課程,說明如何使用 Apache Ant 開始 Java 編程。它不包含有關 Java 或 Ant 的深入知識。本教學課程的目標是讓您了解如何在 Ant 中執行最簡單的步驟。
我們想要將來源與產生的檔案分開,因此我們的 Java 來源檔案將會在 src 資料夾中。所有產生的檔案都應該在 build 中,並在那裡拆分為多個子目錄,以供個別步驟使用:classes 供我們的已編譯檔案使用,jar 供我們自己的 JAR 檔案使用。
我們必須只建立 src 目錄。(因為我在 Windows 上工作,以下是 Windows 語法—翻譯成您的 shell)
md src
以下簡單的 Java 類別只會將固定訊息印出至 STDOUT,因此只要將此程式碼寫入 src\oata\HelloWorld.java 即可。
package oata; public class HelloWorld { public static void main(String[] args) { System.out.println("Hello World"); } }
現在只要嘗試編譯並執行它
md build\classes javac -sourcepath src -d build\classes src\oata\HelloWorld.java java -cp build\classes oata.HelloWorld將會產生
Hello World
建立 jar 檔案並不困難。但建立可啟動 jar 檔案需要更多步驟:建立包含啟動類別的明細檔、建立目標目錄和封存檔案。
echo Main-Class: oata.HelloWorld>myManifest md build\jar jar cfm build\jar\HelloWorld.jar myManifest -C build\classes . java -jar build\jar\HelloWorld.jar
注意:echo Main-Class 指令中的 >- 號周圍不要有空白,因為這樣會使它錯誤!
完成僅 Java 的步驟後,我們必須考慮我們的建置程序。我們必須編譯我們的程式碼,否則我們無法啟動程式。喔—啟動
—對,我們可以提供一個目標來執行此動作。我們應該封裝我們的應用程式。現在只有一個類別—但如果您想要提供下載,沒有人會下載數百個檔案...(想想一個複雜的 Swing GUI—因此讓我們建立一個 jar 檔案。一個可啟動的 jar 檔案會很好...而且擁有 clean
目標是一個很好的做法,它會刪除所有產生的東西。許多失敗都可以透過「clean build」解決。
預設情況下,Ant 使用 build.xml 作為建置檔的名稱,因此我們的 .\build.xml 將會是
<project> <target name="clean"> <delete dir="build"/> </target> <target name="compile"> <mkdir dir="build/classes"/> <javac srcdir="src" destdir="build/classes"/> </target> <target name="jar"> <mkdir dir="build/jar"/> <jar destfile="build/jar/HelloWorld.jar" basedir="build/classes"> <manifest> <attribute name="Main-Class" value="oata.HelloWorld"/> </manifest> </jar> </target> <target name="run"> <java jar="build/jar/HelloWorld.jar" fork="true"/> </target> </project>
現在您可以透過以下方式編譯、封裝和執行應用程式
ant compile ant jar ant run
或使用較簡短的方式
ant compile jar run
查看建置檔時,我們會看到 Ant 和僅 Java 命令之間有一些類似的步驟
僅 Java | Ant |
---|---|
md build\classes javac -sourcepath src -d build\classes src\oata\HelloWorld.java echo Main-Class: oata.HelloWorld>mf md build\jar jar cfm build\jar\HelloWorld.jar mf -C build\classes . java -jar build\jar\HelloWorld.jar |
<mkdir dir="build/classes"/> <javac srcdir="src" destdir="build/classes"/> <!-- automatically detected --> <!-- obsolete; done via manifest tag --> <mkdir dir="build/jar"/> <jar destfile="build/jar/HelloWorld.jar" basedir="build/classes"> <manifest> <attribute name="Main-Class" value="oata.HelloWorld"/> </manifest> </jar> <java jar="build/jar/HelloWorld.jar" fork="true"/> |
現在我們有一個可運作的建置檔,我們可以做一些加強:很多時候您會參照相同的目錄,主類別和 jar 名稱是硬編碼的,而且在呼叫時您必須記住建置步驟的正確順序。
第一和第二個問題將使用屬性來解決,第三個問題將使用特殊屬性(<project>
標籤的屬性)來解決,第四個問題可以使用相依性來解決。
<project name="HelloWorld" basedir="." default="main"> <property name="src.dir" value="src"/> <property name="build.dir" value="build"/> <property name="classes.dir" value="${build.dir}/classes"/> <property name="jar.dir" value="${build.dir}/jar"/> <property name="main-class" value="oata.HelloWorld"/> <target name="clean"> <delete dir="${build.dir}"/> </target> <target name="compile"> <mkdir dir="${classes.dir}"/> <javac srcdir="${src.dir}" destdir="${classes.dir}"/> </target> <target name="jar" depends="compile"> <mkdir dir="${jar.dir}"/> <jar destfile="${jar.dir}/${ant.project.name}.jar" basedir="${classes.dir}"> <manifest> <attribute name="Main-Class" value="${main-class}"/> </manifest> </jar> </target> <target name="run" depends="jar"> <java jar="${jar.dir}/${ant.project.name}.jar" fork="true"/> </target> <target name="clean-build" depends="clean,jar"/> <target name="main" depends="clean,run"/> </project>
現在更簡單了,只要執行 ant,您將會得到
Buildfile: build.xml clean: compile: [mkdir] Created dir: C:\...\build\classes [javac] Compiling 1 source file to C:\...\build\classes jar: [mkdir] Created dir: C:\...\build\jar [jar] Building jar: C:\...\build\jar\HelloWorld.jar run: [java] Hello World main: BUILD SUCCESSFUL
有人告訴我們不要使用System
語句。對於輸出,我們應該使用記錄 API,它具有高度的可自訂性(包括在一般生活(= 非開發)執行期間關閉)。我們使用 Log4J,因為
我們將外部程式庫儲存在新的目錄 lib 中。Log4J 可以從記錄首頁 下載 [1]。建立 lib 目錄,並將 log4j-1.2.17.jar 解壓縮到該目錄中。之後,我們必須修改我們的 Java 原始檔,以便使用該程式庫和我們的建置檔,以便可以在編譯和執行期間存取此程式庫。
在 Log4J 的手冊中記載了如何使用 Log4J。我們在此使用 簡短手冊 [2] 中的 MyApp 範例。我們必須先修改 java 原始檔,以便使用記錄架構
package oata;
import org.apache.log4j.Logger;
import org.apache.log4j.BasicConfigurator;
public class HelloWorld {
static Logger logger = Logger.getLogger(HelloWorld.class);
public static void main(String[] args) {
BasicConfigurator.configure();
logger.info("Hello World"); // the old SysO-statement
}
}
大部分的修改都是「架構開銷」,只需要執行一次。藍色線條是我們的「舊 System-out」語句。
不要嘗試執行 ant,否則只會得到許多編譯器錯誤。Log4J 不在類別路徑中,因此我們必須在此處做一些工作。但不要變更 CLASSPATH
環境變數!這僅適用於此專案,而且您可能會中斷其他環境(這是使用 Ant 時最常見的錯誤之一)。我們在建置檔中加入 Log4J(或更精確地說:所有位於 .\lib 下方的程式庫(jar 檔)
<project name="HelloWorld" basedir="." default="main">
...
<property name="lib.dir" value="lib"/>
<path id="classpath">
<fileset dir="${lib.dir}" includes="**/*.jar"/>
</path>
...
<target name="compile">
<mkdir dir="${classes.dir}"/>
<javac srcdir="${src.dir}" destdir="${classes.dir}" classpathref="classpath"/>
</target>
<target name="run" depends="jar">
<java fork="true" classname="${main-class}">
<classpath>
<path refid="classpath"/>
<path location="${jar.dir}/${ant.project.name}.jar"/>
</classpath>
</java>
</target>
...
</project>
在此範例中,我們不是透過其 Main-Class
清單屬性來啟動我們的應用程式,因為我們無法提供 jar 名稱和類別路徑。因此,將紅色線條中的類別新增至已定義的路徑,並照常啟動。執行 ant 會產生(在一般的編譯作業之後)
[java] 0 [main] INFO oata.HelloWorld - Hello World
那是什麼?
[java]
目前執行的 Ant 工作0
抱歉,不知道,一些 Log4J 的東西[main]
我們的應用程式中的執行緒INFO
該語句的記錄層級oata.HelloWorld
該語句的來源-
分隔符號Hello World
訊息對於其他配置 ... 請參閱 Log4J 的文件,了解如何使用其他 PatternLayouts。
為什麼我們使用 Log4J?「它具有高度的可設定性」?不,一切都經過硬編碼!但那不是 Log4J 的錯,而是我們的錯。我們編寫了 BasicConfigurator.configure();
,它暗示一個簡單但經過硬編碼的設定。使用屬性檔會更方便。在 Java 原始檔中,從 main()
方法中刪除 BasicConfiguration
行(以及相關的 import
語句)。然後,Log4J 會搜尋手冊中所述的設定。然後建立新的檔案 src/log4j.properties。這是 Log4J 設定的預設名稱,使用該名稱會讓生活更輕鬆,不僅架構知道裡面是什麼,您也知道!
log4j.rootLogger=DEBUG, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%m%n
此設定會建立一個名為 stdout
的輸出頻道(Appender
)到主控台,它會印出訊息(%m
),後接換行符號(%n
),與早期的 System.out.println()
相同 :-) 喔,好,但我們還沒完成。我們也應該提供設定檔。因此,我們變更建置檔
... <target name="compile"> <mkdir dir="${classes.dir}"/> <javac srcdir="${src.dir}" destdir="${classes.dir}" classpathref="classpath"/> <copy todir="${classes.dir}"> <fileset dir="${src.dir}" excludes="**/*.java"/> </copy> </target> ...
這會將所有資源(只要它們沒有字尾 .java)複製到建置目錄,因此我們可以從該目錄啟動應用程式,而這些檔案會包含在 jar 中。
在這個步驟中,我們將介紹 JUnit [3] 測試架構與 Ant 結合使用的用法。由於 Ant 內建 JUnit 4.13.1,因此您可以直接開始使用它。在 src\oata\HelloWorldTest.java 中撰寫一個測試類別
package oata; import org.junit.Test; import static org.junit.Assert.fail; public class HelloWorldTest { @Test public void testNothing() { } @Test public void testWillAlwaysFail() { fail("An error message"); } }
由於我們沒有實際的商業邏輯要測試,因此這個測試類別非常小:只顯示如何開始。如需進一步資訊,請參閱 JUnit 文件 [3] 和 junit 任務手冊。現在我們在建置檔中新增一個 junit
指令
... <path id="application" location="${jar.dir}/${ant.project.name}.jar"/> <target name="run" depends="jar"> <java fork="true" classname="${main-class}"> <classpath> <path refid="classpath"/> <path refid="application"/> </classpath> </java> </target> <target name="junit" depends="jar"> <junit printsummary="yes"> <classpath> <path refid="classpath"/> <path refid="application"/> </classpath> <batchtest fork="yes"> <fileset dir="${src.dir}" includes="**/*Test.java"/> </batchtest> </junit> </target> ...
我們重複使用 run
目標中定義的我們自己的 jar 檔案路徑,方法是給它一個 id 並讓它在全球可用。printsummary=yes
讓我們看到比「FAILED」或「PASSED」訊息更詳細的資訊。多少測試失敗?一些錯誤?printsummary 會讓我們知道。設定類別路徑以尋找我們的類別。在此使用 batchtest
來執行測試,因此您只要將它們命名為 *Test.java
,就可以輕鬆新增更多測試類別。這是一個常見的命名方案。
在 ant junit 之後,您將會得到
... junit: [junit] Running oata.HelloWorldTest [junit] Tests run: 2, Failures: 1, Errors: 0, Skipped: 0, Time elapsed: 0,01 sec [junit] Test oata.HelloWorldTest FAILED BUILD SUCCESSFUL ...
我們也可以產生一個報告。在關閉殼層後,您(和其他人)可以閱讀的東西 ... 有兩個步驟:1. 讓 <junit>
記錄資訊,2. 將這些記錄檔轉換為可讀(可瀏覽)的內容。
... <property name="report.dir" value="${build.dir}/junitreport"/> ... <target name="junit" depends="jar"> <mkdir dir="${report.dir}"/> <junit printsummary="yes"> <classpath> <path refid="classpath"/> <path refid="application"/> </classpath> <formatter type="xml"/> <batchtest fork="yes" todir="${report.dir}"> <fileset dir="${src.dir}" includes="**/*Test.java"/> </batchtest> </junit> </target> <target name="junitreport"> <junitreport todir="${report.dir}"> <fileset dir="${report.dir}" includes="TEST-*.xml"/> <report todir="${report.dir}"/> </junitreport> </target>
由於我們會產生大量檔案,而這些檔案預設會寫入目前目錄,因此我們定義一個報告目錄,在執行 junit
之前建立它,並將記錄重新導向到它。記錄格式是 XML,因此 junitreport
可以剖析它。在第二個目標中,junitreport
應該為報告目錄中所有產生的 XML 記錄檔建立一個可瀏覽的 HTML 報告。現在您可以開啟 ${report.dir}\index.html 並查看結果(看起來有點像 JavaDoc)。
我個人對 <junit>
和 <junitreport>
使用兩個不同的目標。產生 HTML 報告需要一些時間,而且您不需要 HTML 報告來進行測試,例如,如果您正在修正錯誤或整合伺服器正在執行作業。