1. 程式人生 > >Maven 專案打包需要注意到的那點事兒

Maven 專案打包需要注意到的那點事兒

               

1. 關於 Maven 打 war 包

使用 Eclipse 的 Maven 2 外掛開發一個 JEE 專案》詳細介紹瞭如何在 Eclipse 使用 Maven 新建一個 JEE 專案並對其進行斷點跟蹤除錯,但是沒有介紹如何對 JEE 專案打 war 包。其實很簡單,你只需要把 pom.xml 中的 <packaging>jar</packaging> 換成 <packaging>war</packaging> 就可以使用 mvn package 命令對其打 war 包了,而不需要新增任何 maven 外掛。只要你遵循了 maven 規範(比如照著《
使用 Eclipse 的 Maven 2 外掛開發一個 JEE 專案
》所述做了),那你打成的 war 包就肯定包含了第三方依賴包:maven打的war包裡邊的第三方依賴包把這個 war 包丟進 tomcat 的 webapps 目錄,重啟 tomcat 即可完成了該專案的部署。你唯一需要注意的是,在重啟 tomcat 之前把你的 war 重新命名為 專案訪問路徑.war。比如作者打成的 war 包是為 swifton-1.0.0.war,對該專案定義的訪問路徑是 /swifton,那麼我在重啟 tomcat 之前需要將其重新命名為 swifton.war。

2. 可執行程式打 jar 包

關於可執行程式(需要指定一個 main 類)打 jar 包就沒這麼方便了,我們需要考慮以下幾個問題:
  • 配置檔案需要打進 jar 包;
  • 需要指定 main 入口類;
  • 所依賴的第三方庫也要打進 jar 包;
只有同時滿足以上三點,我們才可以直接使用 java -jar swiftonrsa-1.0.0.jar 命令成功執行該程式。為了讓討論不那麼抽象,我們在 Eclipse 下新建一個 maven 專案 swiftonrsa:新建maven專案swiftonrsa其中,com.defonds.RsaEncryptor 是入口 main 類,其原始碼如下:
package com.defonds;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;import
com.defonds.service.LinkPayAntharService;public class RsaEncryptor public static void main(String[] args) {  ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");  LinkPayAntharService linkPayAntharService = (LinkPayAntharService) context.getBean("linkPayAntharService");  linkPayAntharService.dealWithYearData(); }}

2.1 配置檔案打包不需要額外關注

只要你專案所依賴的配置檔案都按照 maven 規範放對位置(src/main/resources),那麼打好的 jar 包就會把它們一起打包:配置檔案打包不需要額外關注但是這樣打好的 jar 包既沒有指定 main 入口類,也沒有將依賴包打進來,我們執行它:swiftonrsa-1.0.0.jar中沒有清單屬性提示"swiftonrsa-1.0.0.jar中沒有主清單屬性",我們檢視打好 jar 包下 META-INF 目錄中的 MANIFEST.MF,其內容如下:Manifest-Version: 1.0Built-By: DefondsBuild-Jdk: 1.7.0_67Created-By: Apache Maven 3.2.3Archiver-Version: Plexus Archiver確實沒有指出 main 入口類。

2.2 maven-assembly-plugin 外掛

於是我們引入了 maven-assembly-plugin 外掛,pom.xml 中加入如下程式碼:
 <build>  <plugins>   <plugin>    <artifactId>maven-assembly-plugin</artifactId>    <configuration>     <appendAssemblyId>false</appendAssemblyId>     <descriptorRefs>      <descriptorRef>jar-with-dependencies</descriptorRef>     </descriptorRefs>     <archive>      <manifest>       <mainClass>com.defonds.RsaEncryptor</mainClass>      </manifest>     </archive>    </configuration>    <executions>     <execution>      <id>make-assembly</id>      <phase>package</phase>      <goals>       <goal>assembly</goal>      </goals>     </execution>    </executions>   </plugin>  </plugins> </build>
執行 mvn assembly:assembly,成功構建 swiftonrsa-1.0.0.jar,檢視其打包目錄,各種配置檔案以及第三方依賴包也都有:assembly打包後的目錄結構然後檢視 META-INF 目錄中的 MANIFEST.MF,內容如下:Manifest-Version: 1.0Archiver-Version: Plexus ArchiverCreated-By: Apache MavenBuilt-By: DefondsBuild-Jdk: 1.7.0_67Main-Class: com.defonds.RsaEncryptor懷著興奮的心情執行之:assembly打包後的執行情況maven-assembly-plugin 外掛沒有給我們帶來驚喜。錯誤資訊如下:Exception in thread "main" org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: Unable to locate Spring NamespaceHandler for XML schema namespace [http://www.springframework.org/schema/context]原來這是 assembly 外掛的一個 bug:http://jira.codehaus.org/browse/MASSEMBLY-360,它在對第三方打包時,對於 META-INF 下的 spring.handlers,spring.schemas 等多個同名檔案進行了覆蓋,遺漏掉了一些版本的 xsd 本地對映。

2.3 maven-shade-plugin 外掛

有破必有立。http://jira.codehaus.org/browse/MASSEMBLY-360 跟帖中有網友推薦了 maven-shade-plugin 外掛。於是我們使用 maven-shade-plugin 將 maven-assembly-plugin 替代:
 <build>  <plugins>   <plugin>    <groupId>org.apache.maven.plugins</groupId>    <artifactId>maven-shade-plugin</artifactId>    <version>1.4</version>    <executions>     <execution>      <phase>package</phase>      <goals>       <goal>shade</goal>      </goals>      <configuration>       <transformers>        <transformer         implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">         <mainClass>com.defonds.RsaEncryptor</mainClass>        </transformer>        <transformer         implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">         <resource>META-INF/spring.handlers</resource>        </transformer>        <transformer         implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">         <resource>META-INF/spring.schemas</resource>        </transformer>       </transformers>      </configuration>     </execution>    </executions>   </plugin>  </plugins> </build>
對於多個第三方包 META-INF 下的同名的 spring.handlers 檔案它採取的態度是追加而不是覆蓋。執行 maven clean package,成功構建 swiftonrsa-1.0.0.jar,檢視其打包目錄,各種配置檔案以及第三方依賴包也都有,以及 META-INF 目錄中的 MANIFEST.MF 的內容,基本如 maven-assembly-plugin 打包後的樣子,執行之:shade外掛打包後執行情況錯誤資訊如下:java.lang.SecurityException: Invalid signature file digest for Manifest main attributes這是由於一些包重複引用,打包後的 META-INF 目錄多出了一些 *.SF 等檔案所致。有破必有立。部落格 http://zhentao-li.blogspot.com/2012/06/maven-shade-plugin-invalid-signature.html 給出瞭解決方案,pom.xml 新增:
        <configuration>          <filters>            <filter>              <artifact>*:*</artifact>              <excludes>                <exclude>META-INF/*.SF</exclude>                <exclude>META-INF/*.DSA</exclude>                <exclude>META-INF/*.RSA</exclude>              </excludes>            </filter>          </filters>        </configuration>
於是我們對 maven-shade-plugin 的配置變成這樣:
 <build>  <plugins>   <plugin>    <groupId>org.apache.maven.plugins</groupId>    <artifactId>maven-shade-plugin</artifactId>    <version>1.4</version>    <executions>     <execution>      <phase>package</phase>      <goals>       <goal>shade</goal>      </goals>      <configuration>       <filters>        <filter>         <artifact>*:*</artifact>         <excludes>          <exclude>META-INF/*.SF</exclude>          <exclude>META-INF/*.DSA</exclude>          <exclude>META-INF/*.RSA</exclude>         </excludes>        </filter>       </filters>       <transformers>        <transformer         implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">         <mainClass>com.defonds.RsaEncryptor</mainClass>        </transformer>        <transformer         implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">         <resource>META-INF/spring.handlers</resource>        </transformer>        <transformer         implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">         <resource>META-INF/spring.schemas</resource>        </transformer>       </transformers>      </configuration>     </execution>    </executions>   </plugin>  </plugins> </build>
再次執行 maven clean package,再次執行成功構建後的 swiftonrsa-1.0.0.jar:再次執行shade外掛打包後的swiftonrsa最後兩行是具體業務實現類 com.defonds.service.LinkPayAntharServiceImpl 成功執行打印出的 log 日誌。

2.4 示例專案

本文示例專案 swiftonrsa 已上傳至 CSDN 資源,有興趣的朋友可以下載下來參考實驗,下載地址:http://download.csdn.net/detail/defonds/8404739。本文示例專案最終 pom.xml 如下:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>settle</groupId> <artifactId>swiftonrsa</artifactId> <version>1.0.0</version> <packaging>jar</packaging> <name>swiftonrsa</name> <url>http://maven.apache.org</url> <properties>  <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <build>  <plugins>   <plugin>    <groupId>org.apache.maven.plugins</groupId>    <artifactId>maven-shade-plugin</artifactId>    <version>1.4</version>    <executions>     <execution>      <phase>package</phase>      <goals>       <goal>shade</goal>      </goals>      <configuration>       <filters>        <filter>         <artifact>*:*</artifact>         <excludes>          <exclude>META-INF/*.SF</exclude>          <exclude>META-INF/*.DSA</exclude>          <exclude>META-INF/*.RSA</exclude>         </excludes>        </filter>       </filters>       <transformers>        <transformer         implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">         <mainClass>com.defonds.RsaEncryptor</mainClass>        </transformer>        <transformer         implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">         <resource>META-INF/spring.handlers</resource>        </transformer>        <transformer         implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">         <resource>META-INF/spring.schemas</resource>        </transformer>       </transformers>      </configuration>     </execution>    </executions>   </plugin>  </plugins> </build> <dependencies>  <!-- logs -->  <dependency>   <groupId>log4j</groupId>   <artifactId>log4j</artifactId>   <version>1.2.17</version>  </dependency>  <dependency>   <groupId>commons-logging</groupId>   <artifactId>commons-logging</artifactId>   <version>1.2</version>  </dependency>  <dependency>   <groupId>org.slf4j</groupId>   <artifactId>slf4j-api</artifactId>   <version>1.7.10</version>  </dependency>  <dependency>   <groupId>org.slf4j</groupId>   <artifactId>slf4j-log4j12</artifactId>   <version>1.7.10</version>  </dependency>  <!-- spring -->  <dependency>   <groupId>org.springframework</groupId>   <artifactId>spring-core</artifactId>   <version>3.2.3.RELEASE</version>  </dependency>  <dependency>   <groupId>org.springframework</groupId>   <artifactId>spring-orm</artifactId>   <version>3.2.3.RELEASE</version>  </dependency>  <dependency>   <groupId>org.springframework</groupId>   <artifactId>spring-context</artifactId>   <version>3.2.3.RELEASE</version>  </dependency>  <!-- ibatis -->  <dependency>   <groupId>org.apache.ibatis</groupId>   <artifactId>ibatis-sqlmap</artifactId>   <version>2.3.4.726</version>  </dependency>  <!-- connector -->  <dependency>   <groupId>mysql</groupId>   <artifactId>mysql-connector-java</artifactId>   <version>5.1.19</version>  </dependency>  <!-- dbcp -->  <dependency>   <groupId>commons-dbcp</groupId>   <artifactId>commons-dbcp</artifactId>   <version>1.4</version>  </dependency>  <dependency>   <groupId>commons-pool</groupId>   <artifactId>commons-pool</artifactId>   <version>1.6</version>  </dependency>  <!-- rsa encrypt -->  <dependency>   <groupId>org.bouncycastle</groupId>   <artifactId>bcprov-jdk15on</artifactId>   <version>1.51</version>  </dependency>  <!-- commons-codec -->  <dependency>   <groupId>commons-codec</groupId>   <artifactId>commons-codec</artifactId>   <version>1.6</version>  </dependency> </dependencies></project>