用 Maven 管理項目文件週期的技巧 java
您以爲本身懂 Java 編程?事實是,大多數開發人員都只領會到了 Java 平臺的皮毛,所學也只夠應付工做。在本系列 中,Java 技術深度挖掘 Java 平臺的核心功能,揭示一些不爲人知的事實,幫助您解決最棘手的編程困難。 apache
Maven 是爲 Java™ 開發人員提供的一個極爲優秀的構建工具,您也可使用它來管理您的項目生命週期。做爲一個生命週期管理工具,Maven 是基於階段操做的,而不像 Ant 是基於 「任務」 構建的。Maven 完成項目生命週期的全部階段,包括驗證、代碼生成、編譯、測試、打包、集成測試、安裝、部署、以及項目網站建立和部署。 編程
爲了更好地理解 Maven 和傳統構建工具的不一樣,咱們來看看構建一個 JAR 文件和一個 EAR 文件的過程。使用 Ant,您可能須要定義專有任務來組裝每一個工件。另外一方面,Maven 能夠爲您完成大部分工做:您只須要告訴它是一個 JAR 文件仍是一個 EAR 文件,而後指示它來完成 「打包」 過程。Maven 將會找到所需的資源,而後構建文件。 api
在本文的 參考資料 部分,您將發現大量介紹 Maven 的入門教程。本文的 5 個技巧目的是幫助您解決即將出現的一些問題:使用 Maven 管理您的應用程序的生命週期時,將會出現的編程場景。 tomcat
使用 Maven 構建一個 JAR 文件比較容易:只要定義項目包裝爲 「jar」,而後執行包裝生命週期階段便可。可是定義一個可執行 JAR 文件卻比較麻煩。採起如下步驟能夠更高效: 服務器
您能夠手工進行這些操做,或者要想更高效,您可使用兩個 Maven 插件幫助您完成:maven-jar-plugin 和 maven-dependency-plugin。
maven-jar-plugin 能夠作不少事情,但在這裏,咱們只對使用它來修改默認 MANIFEST.MF 文件的內容感興趣。在您的 POM 文件的插件部分添加清單 1 所示代碼:
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> <configuration> <archive> <manifest> <addClasspath>true</addClasspath> <classpathPrefix>lib/</classpathPrefix> <mainClass>com.mypackage.MyClass</mainClass> </manifest> </archive> </configuration> </plugin>
全部 Maven 插件經過一個 <configuration> 元素公佈了其配置,在本例中,maven-jar-plugin 修改它的 archive 屬性,特別是存檔文件的manifest 屬性,它控制 MANIFEST.MF 文件的內容。包括 3 個元素:
當您使用這 3 個元素配置好了 MANIFEST.MF 文件以後,下一步是將全部的依賴項複製到 lib 文件夾。爲此,使用 maven-dependency-plugin,如清單 2 所示:
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-dependency-plugin</artifactId> <executions> <execution> <id>copy</id> <phase>install</phase> <goals> <goal>copy-dependencies</goal> </goals> <configuration> <outputDirectory> ${project.build.directory}/lib </outputDirectory> </configuration> </execution> </executions> </plugin>
maven-dependency-plugin 有一個 copy-dependencies,目標是將您的依賴項複製到您所選擇的目錄。本例中,我將依賴項複製到 build 目錄下的 lib 目錄(project-home/target/lib)。
將您的依賴項和修改的 MANIFEST.MF 放在適當的位置後,您就能夠用一個簡單的命令啓動應用程序:
java -jar jarfilename.jar
雖然 maven-jar-plugin 容許您修改 MANIFEST.MF 文件的共有部分,但有時候您須要一個更個性化的 MANIFEST.MF。解決方案是雙重的:
例如,考慮一個包含 Java 代理的 JAR 文件。要運行一個 Java 代理,須要定義 Premain-Class 和設置許可。清單 3 展現了這樣一個 MANIFEST.MF 文件的內容:
Manifest-Version: 1.0 Premain-Class: com.geekcap.openapm.jvm.agent.Agent Can-Redefine-Classes: true Can-Retransform-Classes: true Can-Set-Native-Method-Prefix: true
在 清單 3 中,我已指定 Premain-Class - com.geekcap.openapm.jvm.agent.Agent 被受權許可來對類進行重定義和再轉換。接下來,我更新maven-jar-plugin 代碼來包含 MANIFEST.MF 文件。如清單 4 所示:
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> <configuration> <archive> <manifestFile> src/main/resources/META-INF/MANIFEST.MF </manifestFile> <manifest> <addClasspath>true</addClasspath> <classpathPrefix>lib/</classpathPrefix> <mainClass> com.geekcap.openapm.ui.PerformanceAnalyzer </mainClass> </manifest> </archive> </configuration> </plugin>
Maven 2 已確立了它做爲一種最流行和最易使用的開源 Java 生命週期管理工具的地位。Maven 3,2010 年 9 月升級爲 alpha 5,帶來一些期待已久的改進。在 參考資料部分尋找 Maven 的新功能。
這是一個頗有趣的示例,由於它既定義了一個 Premain-Class — 容許 JAR 文件做爲一個 Java 代理運行,也有一個 mainClass — 容許它做爲一個可執行的 JAR 文件運行。在這個特殊的例子中,我使用 OpenAPM(我已構建的一個代碼跟蹤工具)來定義將被 Java 代理和一個用戶界面記錄的代碼跟蹤。簡而言之,這個示例展現一個顯式清單文件與動態修改相結合的力量。
Maven 一個最有用的功能是它支持依賴項管理:您只須要定義您應用程序依賴的庫,Maven 找到它們、下載它們、而後使用它們編譯您的代碼。
必要時,您須要知道具體依賴項的來源 — 這樣您就能夠找到同一個 JAR 文件的不一樣版本的區別和矛盾。這種狀況下,您將須要防止將一個版本的 JAR 文件包含在您的構建中,可是首先您須要定位保存 JAR 的依賴項。
一旦您知道下列命令,那麼定位依賴項每每是至關容易的:
mvn dependency:tree
dependency:tree 參數顯示您的全部直接依賴項,而後顯示全部子依賴項(以及它們的子依賴項,等等)。例如,清單 5 節選自個人一個依賴項所須要的客戶端庫:
[INFO] ------------------------------------------------------------------------ [INFO] Building Client library for communicating with the LDE [INFO] task-segment: [dependency:tree] [INFO] ------------------------------------------------------------------------ [INFO] [dependency:tree {execution: default-cli}] [INFO] com.lmt.pos:sis-client:jar:2.1.14 [INFO] +- org.codehaus.woodstox:woodstox-core-lgpl:jar:4.0.7:compile [INFO] | \- org.codehaus.woodstox:stax2-api:jar:3.0.1:compile [INFO] +- org.easymock:easymockclassextension:jar:2.5.2:test [INFO] | +- cglib:cglib-nodep:jar:2.2:test [INFO] | \- org.objenesis:objenesis:jar:1.2:test
在 清單 5 中您能夠看到 sis-client 項目須要 woodstox-core-lgpl 和 easymockclassextension 庫。easymockclassextension 庫反過來須要 cglib-nodep 庫和 objenesis 庫。若是個人 objenesis 出了問題,好比出現兩個版本,1.2 和 1.3,那麼這個依賴項樹可能會向我顯示,1.2 工件是直接由 easymockclassextension 庫導入的。
dependency:tree 參數爲我節省了不少調試時間,我但願對您也一樣有幫助。
多數重大項目至少有一個核心環境,由開發相關的任務、質量保證(QA)、集成和生產組成。管理全部這些環境的挑戰是配置您的構建,這必須鏈接到正確的數據庫中,執行正確的腳本集、併爲每一個環境部署正確的工件。使用 Maven 配置文件讓您完成這些任務,而無需爲每一個環境分別創建明確指令。
關鍵在於環境配置文件和麪向任務的配置文件的合併。每一個環境配置文件定義其特定的位置、腳本和服務器。所以,在個人 pox.xml 文件中,我將定義面向任務的配置文件 「deploywar」,如清單 6 所示:
<profiles> <profile> <id>deploywar</id> <build> <plugins> <plugin> <groupId>net.fpic</groupId> <artifactId>tomcat-deployer-plugin</artifactId> <version>1.0-SNAPSHOT</version> <executions> <execution> <id>pos</id> <phase>install</phase> <goals> <goal>deploy</goal> </goals> <configuration> <host>${deploymentManagerRestHost}</host> <port>${deploymentManagerRestPort}</port> <username>${deploymentManagerRestUsername}</username> <password>${deploymentManagerRestPassword}</password> <artifactSource> address/target/addressservice.war </artifactSource> </configuration> </execution> </executions> </plugin> </plugins> </build> </profile> </profiles>
這個配置文件(經過 ID 「deploywar」 區別)執行 tomcat-deployer-plugin,被配置來鏈接一個特定主機和端口,以及指定用戶名和密碼證書。全部這些信息使用變量來定義,好比 ${deploymentmanagerRestHost}。這些變量在個人 profiles.xml 文件中定義,如清單 7 所示:
<!-- Defines the development deployment information --> <profile> <id>dev</id> <activation> <property> <name>env</name> <value>dev</value> </property> </activation> <properties> <deploymentManagerRestHost>10.50.50.52</deploymentManagerRestHost> <deploymentManagerRestPort>58090</deploymentManagerRestPort> <deploymentManagerRestUsername>myusername</deploymentManagerRestUsername> <deploymentManagerRestPassword>mypassword</deploymentManagerRestPassword> </properties> </profile> <!-- Defines the QA deployment information --> <profile> <id>qa</id> <activation> <property> <name>env</name> <value>qa</value> </property> </activation> <properties> <deploymentManagerRestHost>10.50.50.50</deploymentManagerRestHost> <deploymentManagerRestPort>58090</deploymentManagerRestPort> <deploymentManagerRestUsername> myotherusername </deploymentManagerRestUsername> <deploymentManagerRestPassword> myotherpassword </deploymentManagerRestPassword> </properties> </profile>
在 清單 7 的 profiles.xml 文件中,我定義了兩個配置文件,並根據 env (環境)屬性的值激活它們。若是 env 屬性被設置爲 dev,則使用開發部署信息。若是 env 屬性被設置爲 qa,那麼將使用 QA 部署信息,等等。
這是部署文件的命令:
mvn -Pdeploywar -Denv=dev clean install
-Pdeploywar 標記通知要明確包含 deploywar 配置文件。-Denv=dev 語句建立一個名爲 env 的系統屬性,並將其值設爲 dev,這激活了開發配置。傳遞 -Denv=qa 將激活 QA 配置。
Maven 有十幾個預構建插件供您使用,可是有時候您只想找到本身須要的插件,構建一個定製的 Maven 插件比較容易:
例如,清單 8 顯示了一個定製插件(爲了部署 Tomcat)的相關部分:
package net.fpic.maven.plugins; import java.io.File; import java.util.StringTokenizer; import net.fpic.tomcatservice64.TomcatDeploymentServerClient; import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; import com.javasrc.server.embedded.CommandRequest; import com.javasrc.server.embedded.CommandResponse; import com.javasrc.server.embedded.credentials.Credentials; import com.javasrc.server.embedded.credentials.UsernamePasswordCredentials; import com.javasrc.util.FileUtils; /** * Goal that deploys a web application to Tomcat * * @goal deploy * @phase install */ public class TomcatDeployerMojo extends AbstractMojo { /** * The host name or IP address of the deployment server * * @parameter alias="host" expression="${deploy.host}" @required */ private String serverHost; /** * The port of the deployment server * * @parameter alias="port" expression="${deploy.port}" default-value="58020" */ private String serverPort; /** * The username to connect to the deployment manager (if omitted then the plugin * attempts to deploy the application to the server without credentials) * * @parameter alias="username" expression="${deploy.username}" */ private String username; /** * The password for the specified username * * @parameter alias="password" expression="${deploy.password}" */ private String password; /** * The name of the source artifact to deploy, such as target/pos.war * * @parameter alias="artifactSource" expression=${deploy.artifactSource}" * @required */ private String artifactSource; /** * The destination name of the artifact to deploy, such as ROOT.war. * If not present then the * artifact source name is used (without pathing information) * * @parameter alias="artifactDestination" * expression=${deploy.artifactDestination}" */ private String artifactDestination; public void execute() throws MojoExecutionException { getLog().info( "Server Host: " + serverHost + ", Server Port: " + serverPort + ", Artifact Source: " + artifactSource + ", Artifact Destination: " + artifactDestination ); // Validate our fields if( serverHost == null ) { throw new MojoExecutionException( "No deployment host specified, deployment is not possible" ); } if( artifactSource == null ) { throw new MojoExecutionException( "No source artifact is specified, deployment is not possible" ); } ... } }
在這個類的頭部,@goal 註釋指定 MOJO 執行的目標,而 @phase 指出目標執行的階段。除了一個映射到一個有真實值的系統屬性的表達式以外,每一個公佈的屬性有一個 @phase 註釋,經過將被執行的參數指定別名。若是屬性有一個 @required 註釋,那麼它是必須的。若是它有一個default-value,那麼若是沒有指定的話,將使用這個值。在 execute() 方法中,您能夠調用 getLog() 來訪問 Maven 記錄器,根據記錄級別,它將輸出具體消息到標準輸出設備。若是插件發生故障,拋出一個 MojoExecutionException 將致使構建失敗。
您可使用 Maven 只進行構建,可是最好的 Maven 是一個項目生命週期管理工具。本文介紹了 5 個你們不多瞭解的特性,能夠幫助您更高效地使用 Maven。在 參考資料 部分獲取 Maven 的更多信息。