Apache Maven 打包可執行jar

在本文的 參考資料 部分,您將發現大量介紹 Maven 的入門教程。本文的 5 個技巧目的是幫助您解決即將出現的一些問題:使用 Maven 管理您的應用程序的生命週期時,將會出現的編程場景。java

1. 可執行的 JAR 文件

使用 Maven 構建一個 JAR 文件比較容易:只要定義項目包裝爲 「jar」,而後執行包裝生命週期階段便可。可是定義一個可執行 JAR 文件卻比較麻煩。採起如下步驟能夠更高效:node

  1. 在您定義可執行類的 JAR 的 MANIFEST.MF 文件中定義一個 main 類。(MANIFEST.MF 是包裝您的應用程序時 Maven 生成的。)
  2. 找到您項目依賴的全部庫。
  3. 在您的 MANIFEST.MF 文件中包含那些庫,便於您的應用程序找到它們。

您能夠手工進行這些操做,或者要想更高效,您可使用兩個 Maven 插件幫助您完成:maven-jar-plugin 和 maven-dependency-pluginweb

maven-jar-plugin

maven-jar-plugin 能夠作不少事情,但在這裏,咱們只對使用它來修改默認 MANIFEST.MF 文件的內容感興趣。在您的 POM 文件的插件部分添加清單 1 所示代碼:數據庫

清單 1. 使用 maven-jar-plugin 修改 MANIFEST.MF
            <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 個元素:express

  • addClassPath:將該元素設置爲 true 告知 maven-jar-plugin 添加一個 Class-Path 元素到 MANIFEST.MF 文件,以及在 Class-Path 元素中包括全部依賴項。
  • classpathPrefix:若是您計劃在同一目錄下包含有您的全部依賴項,做爲您將構建的 JAR,那麼您能夠忽略它;不然使用classpathPrefix 來指定全部依賴 JAR 文件的前綴。在清單 1 中,classpathPrefix 指出,相對存檔文件,全部的依賴項應該位於 「lib」 文件夾。
  • mainClass:當用戶使用 lib 命令執行 JAR 文件時,使用該元素定義將要執行的類名。

maven-dependency-plugin

當您使用這 3 個元素配置好了 MANIFEST.MF 文件以後,下一步是將全部的依賴項複製到 lib 文件夾。爲此,使用 maven-dependency-plugin,如清單 2 所示:apache

清單 2. 使用 maven-dependency-plugin 將依賴項複製到庫
            <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 放在適當的位置後,您就能夠用一個簡單的命令啓動應用程序:api

java -jar jarfilename.jar
 

2. 定製 MANIFEST.MF

雖然 maven-jar-plugin 容許您修改 MANIFEST.MF 文件的共有部分,但有時候您須要一個更個性化的 MANIFEST.MF。解決方案是雙重的:服務器

  1. 在一個 「模板」 MANIFEST.MF 文件中定義您的全部定製配置。
  2. 配置 maven-jar-plugin 來使用您的 MANIFEST.MF 文件,而後使用一些 Maven 配置加強。

例如,考慮一個包含 Java 代理的 JAR 文件。要運行一個 Java 代理,須要定義 Premain-Class 和設置許可。清單 3 展現了這樣一個 MANIFEST.MF 文件的內容:

清單 3. 在一個定製的 MANIFEST.MF 文件中定義 Premain-Class
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 所示:

清單 4. 包含 Premain-Class
            <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 3

Maven 2 已確立了它做爲一種最流行和最易使用的開源 Java 生命週期管理工具的地位。Maven 3,2010 年 9 月升級爲 alpha 5,帶來一些期待已久的改進。在 參考資料 部分尋找 Maven 的新功能。

這是一個頗有趣的示例,由於它既定義了一個 Premain-Class — 容許 JAR 文件做爲一個 Java 代理運行,也有一個 mainClass — 容許它做爲一個可執行的 JAR 文件運行。在這個特殊的例子中,我使用 OpenAPM(我已構建的一個代碼跟蹤工具)來定義將被 Java 代理和一個用戶界面記錄的代碼跟蹤。簡而言之,這個示例展現一個顯式清單文件與動態修改相結合的力量。

 

3. 依賴項樹

Maven 一個最有用的功能是它支持依賴項管理:您只須要定義您應用程序依賴的庫,Maven 找到它們、下載它們、而後使用它們編譯您的代碼。

必要時,您須要知道具體依賴項的來源 — 這樣您就能夠找到同一個 JAR 文件的不一樣版本的區別和矛盾。這種狀況下,您將須要防止將一個版本的 JAR 文件包含在您的構建中,可是首先您須要定位保存 JAR 的依賴項。

一旦您知道下列命令,那麼定位依賴項每每是至關容易的:

mvn dependency:tree

dependency:tree 參數顯示您的全部直接依賴項,而後顯示全部子依賴項(以及它們的子依賴項,等等)。例如,清單 5 節選自個人一個依賴項所須要的客戶端庫:

清單 5. Maven 依賴項樹
[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 參數爲我節省了不少調試時間,我但願對您也一樣有幫助。

 

4. 使用配置文件

多數重大項目至少有一個核心環境,由開發相關的任務、質量保證(QA)、集成和生產組成。管理全部這些環境的挑戰是配置您的構建,這必須鏈接到正確的數據庫中,執行正確的腳本集、併爲每一個環境部署正確的工件。使用 Maven 配置文件讓您完成這些任務,而無需爲每一個環境分別創建明確指令。

關鍵在於環境配置文件和麪向任務的配置文件的合併。每一個環境配置文件定義其特定的位置、腳本和服務器。所以,在個人 pox.xml 文件中,我將定義面向任務的配置文件 「deploywar」,如清單 6 所示:

清單 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 所示:

清單 7. profiles.xml
        <!-- 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>

部署 Maven 配置文件

在 清單 7 的 profiles.xml 文件中,我定義了兩個配置文件,並根據 env (環境)屬性的值激活它們。若是 env 屬性被設置爲 dev,則使用開發部署信息。若是 env 屬性被設置爲 qa,那麼將使用 QA 部署信息,等等。

這是部署文件的命令:

mvn -Pdeploywar -Denv=dev clean install

-Pdeploywar 標記通知要明確包含 deploywar 配置文件。-Denv=dev 語句建立一個名爲 env 的系統屬性,並將其值設爲 dev,這激活了開發配置。傳遞 -Denv=qa 將激活 QA 配置。

 

5. 定製 Maven 插件

Maven 有十幾個預構建插件供您使用,可是有時候您只想找到本身須要的插件,構建一個定製的 Maven 插件比較容易:

  1. 用 POM packaging 建立一個新項目,設置爲 「maven-plugin」。
  2. 包括一個 maven-plugin-plugin 調用,能夠定義您的公佈插件目標。
  3. 建立一個 Maven 插件 「mojo」 類 (一個擴展 AbstractMojo 的類)。
  4. 爲類的 Javadoc 作註釋來定義目標,併爲每一個將被做爲配置參數公佈的變量作註解。
  5. 實現一個 execute() 方法,該方法在調用您的插件是將被調用。

例如,清單 8 顯示了一個定製插件(爲了部署 Tomcat)的相關部分:

清單 8. TomcatDeployerMojo.java
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 將致使構建失敗。

相關文章
相關標籤/搜索