程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> 初學maven(4)-使用maven ant task實現非標准打包

初學maven(4)-使用maven ant task實現非標准打包

編輯:關於JAVA

maven很強大,但是總有些事情干起來不是得心應手,沒有使用ant時那種想怎麼干就怎麼干的流暢感。尤其當要打包一個特殊(相對maven的標准架構而且)時,常有不知所措的感覺。當然這個應該和自己對maven的了解不夠有關,畢竟,“初學maven”嘛。

但是maven在依賴管理方面實在是太強大了,太喜歡,退回原來的ant方式完全不可能,我想用過maven的人,一般是不會有回到原來在cvs,subversion中checkin/checkout n個jar包的時代,僅此一項理由就足夠繼續堅持使用maven了。

然而ant的靈活又難於忘懷,尤其是從ant的build.xml一路走來的人,總是回不知不覺間想到ant的美好。魚與熊掌,我都想要。最近想打包一個java應用,很簡單但即不是標准的j2ee appication也不是web application, 用maven完全不知道該怎麼打包,package出來的完全不是我想要的,在網上四處google maven資料的時候,總有用回ant拉倒的沖動。

先交代一下背景吧,我要打包的程序,是這個樣子:

demo1
   |____lib
   |_____demo1.jar
   |_____*****.jar
   |_____*****.jar
   |____config
   |_____*****.properties
   |_____*****.xml
   |____log
   |_____*****.log
   |____run.bat
   |____run.sh

這個應用的打包模式很簡單,一個bat或者sh腳本用來執行,lib目錄下存放所有的jar包,包括自己的源代碼編譯打包的jar和第三方包。config下是配置文件,這些文件需要在安裝時或者運行前修改,比如監聽的端口啊,數據庫信息之類的。log目錄存放日志文件。最後打包的產物是一個zip包(或者tar,tar.gz)。

遇到的問題,就是maven標准的打包方式中根本不考慮類似的情況,什麼jar,ear,war完全不適用。而且maven有些理念也詫異,比如maven標准的config目錄是src/main/config,但是這個目錄裡面的配置文件默認會打包到jar包中,暈,都在jar裡面了還讓人怎麼改啊?

本著盡量只用maven不用ant的想法,我在maven的資料中看了好久,沒有找到解決的方法。暈,難道大家都只打包標准的ear,jar,war,只有我這樣無聊的人才會有這種打包的需求?

幾經尋覓和探索,最後發現,maven ant tasks似乎是一個不錯的選擇。帶著mavenanttasks的官方文檔和google上搜到的幾篇文章,開始嘗試,成功實現功能。現在將過程和方法share給大家。

首先建立java項目anttaskdemo1,按照maven的推薦,文件結構如下:

anttaskdemo1
   |____src/main/java
   |____src/main/config
   |____src/main/bin
   |____src/main/resources
   |____src/test/java
   |____src/test/resources
   |____target
   |____build.properties
   |____build.xml
   |____pom.xml

其中src/main/java下放java代碼;src/main/resources下放一個*.properties文件,這個資源文件是打包到jar中,內容打包之後不需要改變的。src/main/config下放一個標准的log4j.xml,這個是有在安裝運行前臨時修改的需要的。src/main/bin下放置可執行文件。

1. 首先看pom.xml,標准內容,很簡單,象征性的加入幾個依賴

<dependency>
       <groupId>commons-codec</groupId>
       <artifactId>commons-codec</artifactId>
       <version>1.3</version>
     </dependency>
     <dependency>
       <groupId>log4j</groupId>
       <artifactId>log4j</artifactId>
       <version>1.2.13</version>
       <scope>compile</scope>
     </dependency>
     <dependency>
       <groupId>org.testng</groupId>
       <artifactId>testng</artifactId>
       <version>5.8</version>
       <scope>test</scope>
       <classifier>jdk15</classifier>
     </dependency>

其中commons-codec,log4j是需要打包到lib中的,運行時需要。testng(或者更一般的junit)是用來單元測試的,不需要打包到lib中。請注意它的設置"<scope>test</scope>"。

2. 然後看看build.properties文件,這個內容不多:

M2_REPO=G:/soft/maven/localRepository

path.package=package
path.target.name=anttaskdemo1
path.package.lib=lib
path.package.log=log
path.package.config=config
path.package.bin=

M2_REPO稍後再詳談,後面的path.package之類的是最終打包時使用的幾個目錄名稱,對應於最終打包的結構。

3. build.xml,這個內容比較多。
<?xmlversion="1.0"encoding="UTF-8"?>
<projectname="demo"default="all"xmlns:artifact="urn:maven-artifact-ant">
   <description>
   </description>
   <propertyfile="build.properties"/>
   <targetname="init_maven">
     <!--
     remembertosetM2_REPObeforeusethisbuild.xml,forexampleineclispe:
     "Window→Preferences→Ant→Runtime",addanewpropertynamed"M2_REPO"andsetitvaluepointtothepathofyourmaven
     localrepository;Oryoucansetitinbuild.properties. Youneeddoone(andonlyone)ofthem.
     -->
     <pathid="maven-ant-tasks.classpath"path="${M2_REPO}/org/apache/maven/maven-ant-tasks/2.0.9/maven-ant-tasks-2.0.9.jar"/>
     <typedefresource="org/apache/maven/artifact/ant/antlib.xml"uri="urn:maven-artifact-ant"classpathref="maven-ant-tasks.classpath"/>
     <artifact:pomid="maven.project"file="pom.xml"/>
     <artifact:dependenciespathId="classpath.build"filesetid="maven.fileset.build">
       <pomrefid="maven.project"/>
     </artifact:dependencies>
     <artifact:dependenciespathId="classpath.runtime"filesetid="maven.fileset.runtime"useScope="runtime">
       <pomrefid="maven.project"/>
     </artifact:dependencies>
   </target>
   <targetname="all"depends="init_path,compile,jar,package,zip"description="doall">
     <echo>begintodoalltargettobuildtheresultpackage.</echo>
   </target>
   <targetname="maven_info"depends="init_maven">
     <echo>Mavenbuilddirectoryis${maven.project.build.directory}</echo>
     <echo>MavenbuildfinalNameis${maven.project.build.finalName}</echo>
     <echo>MavenbuildsourceDirectorydirectoryis${maven.project.build.sourceDirectory}</echo>
     <echo>MavenbuildoutputDirectorydirectoryis${maven.project.build.outputDirectory}</echo>
     <echo>MavenbuildscriptSourceDirectorydirectoryis${maven.project.build.testSourceDirectory}</echo>
     <echo>MavenbuildtestOutputDirectorydirectoryis${maven.project.build.testOutputDirectory}</echo>
     <echo>MavenbuildscriptSourceDirectorydirectoryis${maven.project.build.scriptSourceDirectory}</echo>
     <echo>MavenbuildresourceDirectorydirectoryis${maven.project.build.resources}</echo>
     <propertyname="target.jar.name"value="${maven.project.build.directory}/${maven.project.build.finalName}.jar"/>
     <echo>MavenbuildscriptSourceDirectorydirectoryis${target.jar.name}</echo>
   </target>
   <targetname="clean"depends="init_maven">
     <echo>cleanbuilddirectory:${maven.project.build.directory}</echo>
     <deletedir="${maven.project.build.directory}"includes="**/*"/>
   </target>
   <targetname="init_path"depends="maven_info,clean">
     <echo>makedirforjavacompile:${maven.project.build.outputDirectory}</echo>
     <mkdirdir="${maven.project.build.outputDirectory}"/>
   </target>
   <targetname="compile"description="description"depends="init_maven">
     <javacsrcdir="${maven.project.build.sourceDirectory}"destdir="${maven.project.build.outputDirectory}"classpathref="classpath.build"/>
   </target>
   <targetname="copyResource"depends="init_maven">
     <copytodir="${maven.project.build.outputDirectory}">
       <filesetdir="src/main/resources">
       </fileset>
     </copy>
   </target>
   <targetname="jar"depends="compile,copyResource">
     <deletefile="${maven.project.build.directory}/${maven.project.build.finalName}.jar" failonerror="false"/>
     <jardestfile="${maven.project.build.directory}/${maven.project.build.finalName}.jar"basedir="${maven.project.build.outputDirectory}">
     </jar>
   </target>
   <targetname="package"depends="package_prepare,copyLib,copyConfig,copyBin">
   </target>
   <targetname="package_prepare"depends="init_maven">
     <echo>cleanpackagedirectory:${maven.project.build.directory}/${path.package}</echo>
     <deletedir="${maven.project.build.directory}/${path.package}"/>
     <mkdirdir="${maven.project.build.directory}/${path.package}"/>
     <mkdirdir="${maven.project.build.directory}/${path.package}/${path.target.name}"/>
     <mkdirdir="${maven.project.build.directory}/${path.package}/${path.target.name}/${path.package.lib}"/>
     <mkdirdir="${maven.project.build.directory}/${path.package}/${path.target.name}/${path.package.config}"/>
     <mkdirdir="${maven.project.build.directory}/${path.package}/${path.target.name}/${path.package.log}"/>
   </target>
   <targetname="copyLib"depends="init_maven">
     <copytodir="${maven.project.build.directory}/${path.package}/${path.target.name}/${path.package.lib}">
       <filesetrefid="maven.fileset.runtime"/>
       <mappertype="flatten"/>
     </copy>
     <copytodir="${maven.project.build.directory}/${path.package}/${path.target.name}/${path.package.lib}"
       file="${maven.project.build.directory}/${maven.project.build.finalName}.jar">
     </copy>
   </target>
   <targetname="copyConfig"depends="init_maven">
     <copytodir="${maven.project.build.directory}/${path.package}/${path.target.name}/${path.package.config}">
       <filesetdir="src/main/config">
       </fileset>
     </copy>
   </target>
   <targetname="copyBin"depends="init_maven">
     <copytodir="${maven.project.build.directory}/${path.package}/${path.target.name}/${path.package.bin}">
       <filesetdir="src/main/bin">
       </fileset>
     </copy>
   </target>
   <targetname="zip"depends="init_maven">
     <zipdestfile="${maven.project.build.directory}/${path.package}/${path.target.name}.zip">
       <filesetdir="${maven.project.build.directory}/${path.package}/${path.target.name}"></fileset>
     </zip>
   </target>
</project>

一步一步來講吧:

3.1 最重要的一步,init_maven

<targetname="init_maven">
     <!--
     remembertosetM2_REPObeforeusethisbuild.xml,forexampleineclispe:
     "Window→Preferences→Ant→Runtime",addanewpropertynamed"M2_REPO"andsetitvaluepointtothepathofyourmaven
     localrepository;Oryoucansetitinbuild.properties. Youneeddoone(andonlyone)ofthem.
     -->
     <pathid="maven-ant-tasks.classpath"path="${M2_REPO}/org/apache/maven/maven-ant-tasks/2.0.9/maven-ant-tasks-2.0.9.jar"/>
     <typedefresource="org/apache/maven/artifact/ant/antlib.xml"uri="urn:maven-artifact-ant"classpathref="maven-ant-tasks.classpath"/>
     <artifact:pomid="maven.project"file="pom.xml"/>
     <artifact:dependenciespathId="classpath.build"filesetid="maven.fileset.build">
       <pomrefid="maven.project"/>
     </artifact:dependencies>
     <artifact:dependenciespathId="classpath.runtime"filesetid="maven.fileset.runtime"useScope="runtime">
       <pomrefid="maven.project"/>
     </artifact:dependencies>
   </target>

在ant中使用maven-ant-tasks,就需要裝載maven-ant-tasks的jar包,方法有兩種,一種是直接將maven-ant-tasks-2.0.9.jar放到ant的lib下,自然就可以直接使用。但是個人感覺不怎麼喜歡這種方式,我采用的是第二種,在ant的build.xml文件中裝載:

<pathid="maven-ant-tasks.classpath"path="${M2_REPO}/org/apache/maven/maven-ant-tasks/2.0.9/maven-ant-tasks-2.0.9.jar"/>
     <typedefresource="org/apache/maven/artifact/ant/antlib.xml"uri="urn:maven-artifact-ant"classpathref="maven-ant-tasks.classpath"/>

這樣就可以在後面使用maven-ant-tasks的task,比如artifact:pom,artifact:dependencies。<path />用於指定maven-ant-tasks-2.0.9.jar的路徑,這裡就有點麻煩了:maven-ant-tasks-2.0.9.jar這個東西放哪裡好呢?直接放到項目中,然後存放到cvs/svn?顯然不是一個好辦法。考慮到maven本來就是干保存jar這行的,交給maven好了。maven-ant-tasks-2.0.9.jar本來就是一個依賴,可以在http://mvnrepository.com/找到:

http://mvnrepository.com/artifact/org.apache.maven/maven-ant-tasks

pom內容如下:

<dependency>
   <groupId>org.apache.maven</groupId>
   <artifactId>maven-ant-tasks</artifactId>
   <version>2.0.9</version>
</dependency>

將這個內容隨便找個空項目,加入後update一下dependency,在本地的maven repository中就會有它的jar文件。然後就可以直接使用它。

${M2_REPO},這個是eclipse中通用的指向maven local repository的變量,大家使用maven命令建立eclipse項目時會遇到它。我們在這裡可以直接使用這個變量來訪問maven local repository。如果沒有建立這個變量,請自行建立,上面注釋中有詳細說明。如果在eclispe之外比如命令行直接運行ant打包,則可以通過設置build.properties文件中的“M2_REPO=G:/soft/maven/localRepository”來指定。(這裡用到ant的一個特性,屬性一旦被賦值就不能修改,因此第一次賦值有效,在eclispe中運行,M2_REPO會使用eclispe中設置的值,如果eclispe沒有設置或者命令行直接運行,M2_REPO屬性會在build.properties文件裝載時設置。)

裝載ok後,接著是調用artifact:pom和artifact:dependencies 任務來指定pom.xml文件,再得到dependencies信息,後面的編譯打包會使用到。注意useScope="runtime",這個是為了最後打包時使用,只復制runtie時需要的jar包。

<target name="maven_info" />是用來打印maven相關的一些信息的,比如maven下的幾個build目錄。artifact:pom任務裝載了整個pom,因此在後面可以訪問到pom的信息,比如${maven.project.build.sourceDirectory}是java源代碼目錄,${maven.project.build.finalName}是最終的名稱。

pom的內容可以參考這兩個官方資料:

1) maven model

http://maven.apache.org/ref/2.0.9/maven-model/maven.html

2) Introduction to the POM

http://maven.apache.org/guides/introduction/introduction-to-the-pom.html

標准目錄布局可以參考這個官方資料:

http://maven.apache.org/guides/introduction/introduction-to-the-standard-directory-layout.html

3.2 compile

這裡是maven ant task的精髓所在,

<targetname="compile"description="description"depends="init_maven">
     <javacsrcdir="${maven.project.build.sourceDirectory}"destdir="${maven.project.build.outputDirectory}"classpathref="classpath.build"/>
   </target>

結合之前maven初始化時的情況:

<artifact:pomid="maven.project"file="pom.xml"/>
     <artifact:dependenciespathId="classpath.build"filesetid="maven.fileset.build">
       <pomrefid="maven.project"/>
     </artifact:dependencies>

可以看到,我們的項目完全遵循maven的標准做法,然後ant通過訪問pom來得到相關的路徑信息和classpath信息,完美結合。

target copyResource 完成了將resource copy到outputDirectory的任務,這裡的resource都是classpath resource。

注: 這個有個問題,就是我沒有找到通過pom得到resouce目錄的方法,${maven.project.build.resources 只能得到pom文件中<resources/>, 對於裡面包含的<resource>不知道該用什麼方式來訪問,如默認的:

<resources>
    <resource>
     <directory>src/main/resources</directory>
    </resource>
   </resources>

我試過用${maven.project.build.resources[0];${maven.project.build.resources(0), ${maven.project.build.resources.0,${maven.project.build.resources.resource

等都無效。找了很久都沒有找到資料,只好作罷。直接寫死src/main/resources吧。

3.3 jar

和compile類似,標准的ant jar,然後通過訪問pom來得到相關的路徑信息和打包文件名。注意這裡的文件名和路徑和maven的做法一致,也就是說和執行maven的compile命令結果是一樣的。

<targetname="jar"depends="compile,copyResource">
     <deletefile="${maven.project.build.directory}/${maven.project.build.finalName}.jar" failonerror="false"/>
     <jardestfile="${maven.project.build.directory}/${maven.project.build.finalName}.jar"basedir="${maven.project.build.outputDirectory}">
     </jar>
   </target>

3.4 package, zip

package_prepare, copyLib, copyConfig, copyBin 這幾個target都簡單,體力活而已。zip也是。

4. 總結

上面的內容雖多,但是總結起來就只有兩點:

1. maven ant task可以將maven的功能和ant的靈活性統一起來,對於非標准的打包情況非常適用

2. maven ant task的使用並不難,不過需要掌握不少maven的基本知識,比如pom,標准目錄布局等。

另外,似乎,ant + Ivy會是更好的選擇?

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved