介紹使用 Ant 打包可運行的 Jar 包。
打包 jar 包最大的問題在於如何加入第三方 jar 包使得 jar 文件可以直接運行,以下用實例進行說明。
程序結構:
關鍵代碼:

package com.alfred.main; import com.alfred.bean.User; import com.alfred.util.ProjConfig; import com.thoughtworks.xstream.XStream; public class Main { public static void main(String[] args) { User user = new User(); user.setUsername("alfred"); user.setAge(15); XStream xstream = new XStream(); String xml = xstream.toXML(user); System.out.println("in Main"); System.out.println("prop:"+ProjConfig.MY_PROPERTY); System.out.println("xml:"+xml); } }

<?xml version="1.0" encoding="UTF-8"?> <project basedir="." default="usage" name="antproj"> <!-- 引入配置文件 --> <property file="build.properties" /> <!-- 配置引用屬性 --> <property name="src.dir" value="src" /> <property name="config.dir" value="config" /> <property name="build.dir" value="build" /> <property name="lib.dir" value="lib" /> <property name="name" value="antproj" /> <path id="master-classpath"> <fileset dir="${lib.dir}"> <include name="*.jar" /> </fileset> <pathelement path="${build.dir}" /> </path> <target name="usage"> <!-- 輸出信息到控制台中 --> <echo message="" /> <echo message="${name} build file" /> <echo message="-----------------------------------" /> <echo message="" /> <echo message="Available targets are:" /> <echo message="" /> <echo message="build --> Build the application" /> <echo message="" /> </target> <!-- 編譯生成class文件 --> <target name="build" description="Compile main source tree java files"> <!-- 刪除編譯存放的classes文件 --> <delete dir="${build.dir}/classes"></delete> <!-- 新建編譯存放的classes文件 --> <mkdir dir="${build.dir}/classes" /> <copy todir="${build.dir}/classes"> <!-- 將項目中除會編譯生成class文件的java文件之外其他類型的文件拷貝到對應的目錄下,指定文件名后綴 --> <fileset dir="${src.dir}"> <include name="**/*.properties" /> <include name="**/*.xml" /> </fileset> <fileset dir="${config.dir}"> <include name="**/*.properties" /> </fileset> </copy> <!-- java編譯 --> <javac destdir="${build.dir}/classes" source="1.5" target="1.5" debug="true" deprecation="false" optimize="false" failonerror="true" encoding="utf-8"> <src path="${src.dir}" /> <!-- 編譯所需要的jar包路徑 --> <classpath refid="master-classpath" /> </javac> </target> <target name="jar" depends="build" description="Compress the java class files to jar"> <mkdir dir="${build.dir}/jar" /> <delete file="${build.dir}/jar/${name}.jar" /> <buildnumber file="${build.dir}/buildnum.txt" /> <!-- 指定時間戳 可以調用TODAY --> <tstamp> <format property="TODAY" pattern="yyyy-MM-dd HH:mm:ss" /> </tstamp> <!-- 生成清單文件 --> <manifest file="${build.dir}/MANIFEST.MF"> <attribute name="Built-By" value="${user.name}" /> <attribute name="Implementation-Version" value="${version.num}.${build.number}" /> <attribute name="Built-Date" value="${TODAY}" /> <attribute name="Main-Class" value="com.alfred.main.Main" /> </manifest> <!-- 打包jar --> <!-- includes 包含哪些后綴文件 空格分隔 --> <jar destfile="${build.dir}/jar/${name}.jar" basedir="${build.dir}/classes" includes="**/*.class **/*.properties **/*.xml" manifest="${build.dir}/MANIFEST.MF"> <zipfileset src="${lib.dir}/xstream-1.4.9.jar"></zipfileset> <zipfileset src="${lib.dir}/xpp3-1.1.3.3.jar"></zipfileset> </jar> </target> <target name="cleanup"> <!-- 刪除編譯文件夾下的所有文件(不包括編譯文件夾下的文件夾) <delete> <fileset dir="${build.dir}/classes" includes="**/*.*" /> </delete> --> <!-- 刪除編譯文件夾 --> <delete dir="${build.dir}/classes" /> </target> </project>
將第三方 jar 包加入的關鍵代碼是:
<zipfileset src="${lib.dir}/xstream-1.4.9.jar"></zipfileset> <zipfileset src="${lib.dir}/xpp3-1.1.3.3.jar"></zipfileset>
這種情況下生成的 jar 包內部結構如下:
可以看到第三方 jar 包實際上都被拆開打包進我們的 jar 包了。如果使用的第三方 jar 包太多的話,會變得非常混亂。這時可以通過單獨將程序打包為 jar 包,通過引用的方法調用外部第三方 jar 包的方式運行。
如果使用如下的寫法:
<zipfileset dir="${lib.dir}"> <include name="*.jar"/> </zipfileset>
那么打包生成的 jar 包將無法正常調用第三方 jar 包,jar 包結構如下:
可以看到第三方 jar 包都是完整的。
由以上示例可知,無論是將第三方 jar 包拆分打包(結構混亂)還是保持第三方 jar 包完整打包(無法正常運行),都存在一定的問題,以下介紹一種通過引用的方式調用第三方 jar 包的方式。
在 build.xml 中新建一個任務
<target name="jar2" depends="build" description="Compress the java class files to jar"> <mkdir dir="${build.dir}/jar" /> <delete file="${build.dir}/jar/${name}.jar" /> <buildnumber file="${build.dir}/buildnum.txt" /> <!-- 指定時間戳 可以調用TODAY --> <tstamp> <format property="TODAY" pattern="yyyy-MM-dd HH:mm:ss" /> </tstamp> <pathconvert property="quote.classpath" pathsep=" "> <mapper> <chainedmapper> <!-- jar包文件只留文件名,去掉目錄信息 --> <flattenmapper /> <!-- add lib/ prefix --> <globmapper from="*" to="lib/*" /> </chainedmapper> </mapper> <path refid="master-classpath" /> </pathconvert> <!-- 生成清單文件 --> <manifest file="${build.dir}/MANIFEST.MF"> <attribute name="Built-By" value="${user.name}" /> <attribute name="Implementation-Version" value="${version.num}.${build.number}" /> <attribute name="Built-Date" value="${TODAY}" /> <attribute name="Main-Class" value="com.alfred.main.Main" /> <attribute name="Class-Path" value="${quote.classpath}" /> </manifest> <!-- 打包jar --> <!-- includes 包含哪些后綴文件 空格分隔 --> <jar destfile="${build.dir}/jar/${name}.jar" basedir="${build.dir}/classes" includes="**/*.class **/*.properties **/*.xml" manifest="${build.dir}/MANIFEST.MF"> </jar> </target>
將關聯的第三方 jar 包通過路徑轉換加上路徑前綴(也就是之后你 jar 包引用第三方 jar 包的路徑),生成新的路徑信息 quote.classpath,將路徑信息配置進 Class-Path 屬性,通過這種方式生成的 jar 包中 MANIFEST.MF 文件內容中 Class-Path 如下:
Class-Path: lib/xpp3-1.1.3.3.jar lib/xstream-1.4.9.jar
之后我們將第三方 jar 包放置於 lib 目錄中,將打包后的 antproj.jar 放置於和 lib 同級目錄,運行 jar 正常。
另外,要打包可運行的 jar 包,加入第三方 jar 包的話,也可以通過一些 IDE 的插件,例如:fatjar。