【Maven技術專題】「實戰開發系列」盤點Maven項目中打包需要注意到的那點事兒

Maven項目打包需要注意到的那點事兒

  • Maven是什么
  • Maven打包插件的作用
    • Maven打包后經常出現的問題
      • maven構建可運行Jar包
    • Maven打包的三種方式
    • Maven打包的最簡單的方法
      • maven-jar-plugin
        • MANIFEST.MF文件部分
        • MANIFEST.MF的文件內容
        • jar包的拷貝機制
          • 在pom.xml中配置
      • maven-jar-plugin的局限性
      • maven-shade-plugin
        • 使用maven-shade-plugin插件打包
      • maven-assembly-plugin
        • maven指令

Maven是什么

Maven是一個流行的Java構建工具,它提供了許多插件來幫助開發人員自動化構建和部署Java應用程序。其中一個重要的插件是Maven打包插件,它可以將Java項目打包成可執行的JAR或WAR文件。在本文中,我們將深入探討Maven打包插件的技術細節和使用方法。

Maven打包插件的作用

Maven打包插件是一個用于打包Java項目的Maven插件。它可以將項目的源代碼、依賴項和其他資源打包成一個可執行的JAR或WAR文件。這個插件可以自動處理項目的依賴關系,并將它們打包到生成的文件中。此外,它還可以執行其他任務,如壓縮文件、生成文檔等。

首先先梳理一下關于打包 相關的常用的Maven插件工具:

  1. 使用清理插件:maven-clean-plugin: 執行清理刪除已有target目錄;
  2. 使用資源插件:maven-resources-plugin: 執行資源文件的處理
  3. 使用編譯插件:maven-compiler-plugin: 編譯所有源文件生成class文件至target\classes目錄下
  4. 使用資源插件:maven-resources-plugin: 執行測試資源文件的處理
  5. 使用編譯插件:maven-compiler-plugin: 編譯測試目錄下的所有源代碼
  6. 使用插件:maven-surefire-plugin: 運行測試用例
  7. 使用插件:maven-jar-plugin: 對編譯后生成的文件進行打包,

包名稱默認為:artifactId-version,包文件保存在target目錄下(這個生成的包不能在命令行中直接執行,因為我們還沒有入口類配置到Manifest資源配置文件中去,后續會闡述)。

注意:不管是compile、package還是install等前三個步驟都是必不可少的。

Maven打包后經常出現的問題

  • Maven可以使用mvn package指令對項目進行打包,如果使用Java -jar xxx.jar執行運行jar文件,會出現" no main manifest attribute, in xxx.jar"(沒有設置Main-Class)、ClassNotFoundException(找不到依賴包)等錯誤。

這個原因屬于maven沒有執行構建:maven構建可運行Jar包

maven構建可運行Jar包

要想jar包能直接通過java -jar xxx.jar運行,需要滿足:
1. 在jar包中的META-INF/MANIFEST.MF中指定Main-Class,這樣才能確定程序的入口在哪里
2. 要能加載到依賴包,使用Maven有以下幾種方法可以生成能直接運行的jar包,可以根據需要選擇一種合適的方法


Maven打包的三種方式

Maven打包的最簡單的方法

Maven打包插件的配置非常簡單,只需要在項目的pom.xml文件中添加以下代碼即可。

maven-jar-plugin

首先是在maven項目的pom.xml中添加打包的插件,這里有很多種方式的。最最簡單的就是只使用maven-compiler-plugin、maven-jar-plugin插件,并且指定程序入口。

pom.xml文件為:

<?xml version="1.0" encoding="UTF-8"?>
<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>xx</groupId><artifactId>xx</artifactId><version>1.0-SNAPSHOT</version><build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><configuration><source>1.8</source><target>1.8</target></configuration></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-jar-plugin</artifactId><configuration><archive><manifest><addClasspath>true</addClasspath><useUniqueVersions>false</useUniqueVersions><classpathPrefix>lib/</classpathPrefix><mainClass>com.xx.Main</mainClass></manifest></archive></configuration></plugin></plugins></build>
</project> 

這個配置中,我們使用了Maven的maven-jar-plugin插件來打包項目。在插件的配置中,我們指定了生成的JAR文件的元數據,包括主類和類路徑。這些信息將被寫入JAR文件的MANIFEST.MF文件中,以便Java虛擬機可以正確地執行JAR文件。

MANIFEST.MF文件部分
  • com.xx.Main指定MANIFEST.MF中的Main-Class
  • true會在MANIFEST.MF加上Class-Path項并配置依賴包
  • lib/指定依賴包所在目錄。
MANIFEST.MF的文件內容

通過maven-jar-plugin插件生成的MANIFEST.MF文件片段:

Class-Path: lib/x.jar lib/xx.jar  
Main-Class: com.xxg.Main  

當然生成MANIFEST.MF文件還不夠,maven-dependency-plugin插件用于將依賴包拷貝到${project.build.directory}/lib指定的位置,即lib目錄下。

jar包的拷貝機制

配置完成后,通過mvn package指令打包,會在target目錄下生成jar包,并將依賴包拷貝到target/lib目錄下。

在pom.xml中配置
<build>  <plugins>  <plugin>  <groupId>org.apache.maven.plugins</groupId>  <artifactId>maven-jar-plugin</artifactId>  <version>2.6</version>  <configuration>  <archive>  <manifest>  <addClasspath>true</addClasspath>  <classpathPrefix>lib/</classpathPrefix>  <mainClass>com.xx.Main</mainClass>  </manifest>  </archive>  </configuration>  </plugin>  <plugin>  <groupId>org.apache.maven.plugins</groupId>  <artifactId>maven-dependency-plugin</artifactId>  <version>2.10</version>  <executions>  <execution>  <id>copy-dependencies</id>  <phase>package</phase>  <goals>  <goal>copy-dependencies</goal>  </goals>  <configuration>  <outputDirectory>${project.build.directory}/lib</outputDirectory>  </configuration>  </execution>  </executions>  </plugin>  </plugins>  
</build>  

指定了Main-Class,有了依賴包,那么就可以直接通過java -jar xxx.jar運行jar包。這種方式生成jar包有個缺點,就是生成的jar包太多不便于管理,下面兩種方式只生成一個jar文件,包含項目本身的代碼、資源以及所有的依賴包,接下來我們 好好分析一下這種打包方式的局限性。

maven-jar-plugin的局限性

如果一個maven項目中有多個子目錄,每一個子目錄中的pom.xml對應一個項目,它的作用范圍只有這一個目錄下的。比如掃描配置文件,如果要讓一個目錄下的pom.xml掃描另一個目錄下的配置文件,那是做不到的。在打jar包的時候,只運行當前的pom.xml文件。

當然也有其他的打包方法,比如使用spring-boot-maven-plugin插件在打Jar包時,會引入依賴包

maven-shade-plugin

由于上面的打包過程實在是過于的繁瑣,而且也沒有利用到maven管理項目的特色。接下來我們采用maven中的maven-shade-plugin插件進行資源打包,在pom.xml中,加入如下的信息來加入插件。

使用maven-shade-plugin插件打包

在pom.xml中配置:

<build>  <plugins>  <plugin>  <groupId>org.apache.maven.plugins</groupId>  <artifactId>maven-shade-plugin</artifactId>  <version>2.4.1</version>  <executions>  <execution>  <phase>package</phase>  <goals>  <goal>shade</goal>  </goals>  <configuration>  <transformers>  <transformer implementation="org.apache.maven.plugins.shade.resource.Mani                   festResourceTransformer">  <mainClass>com.xx.Main</mainClass>  </transformer>  </transformers>  </configuration>  </execution>  </executions>  </plugin>  </plugins>  
</build>  

配置完成后,執行mvn package即可打包。在target目錄下會生成兩個jar包,注意不是original-xxx.jar文件,而是另外一個。

和maven-assembly-plugin一樣,生成的jar文件包含了所有依賴,所以可以直接運行。如果項目中用到了Spring Framework,將依賴打到一個jar包中,運行時會出現讀取XML schema文件出錯。

原因是Spring Framework的多個jar包中包含相同的文件spring.handlers和spring.schemas,如果生成一個jar包會互相覆蓋。為了避免互相影響,可以使用AppendingTransformer來對文件內容追加合并:

<build>  <plugins>    <plugin>  <groupId>org.apache.maven.plugins</groupId>  <artifactId>maven-shade-plugin</artifactId>  <version>2.4.1</version>  <executions>  <execution>  <phase>package</phase>  <goals>  <goal>shade</goal>  </goals>  <configuration>  <transformers>  <transformer implementation="org.apache.maven.plugins.shade.resource.Mani               festResourceTransformer">  <mainClass>com.xxg.Main</mainClass>  </transformer>  <transformer implementation="org.apache.maven.plugins.shade.resource.App                endingTransformer">  <resource>META-INF/spring.handlers</resource>  </transformer>  <transformer implementation="org.apache.maven.plugins.shade.resource.App                endingTransformer">  <resource>META-INF/spring.schemas</resource>  </transformer>  </transformers>  </configuration>  </execution>  </executions>  </plugin>  </plugins>  
</build>  

這里面配置了一個configuration標簽內容,在此標簽下面 有一個transformer標簽,用來配置Main函數的入口 ( com.xx.Main),當然此標簽內容很復雜,不是上面寫的那么簡單,上面之所以如此簡單,是因為在所有類中(包括第三方Jar)只有一個Main方法。如果第三方jar中有Main方法,就要進行額外的配置,上面這么配置,不一定能執行成功。

在加入這段代碼到pom.xml之后,我們就可以用maven的命令去打包了。其指令如下:

  • mvn clean compile :清除之前target編譯文件并重新編譯
  • mvn clean package :對項目進行打包(因為配置過插件,所以jar包是可執行的)
  • mvn clean install :安裝項目,然后就可以使用了

上面的方法,我們還需要點擊很多命令去打包。這次利用一個新的插件,可以打包更簡單。同樣,在pom.xml中加入如下代碼。上文的maven-shade-plugin插件代碼可以刪除。

maven-assembly-plugin

使用maven-assembly-plugin插件打包,這里同樣配置了一個manifest標簽來配置Main函數的入口。然后通過如下指令來實現打包。

在pom.xml中配置:

<plugin><artifactId>maven-assembly-plugin</artifactId><version>2.4</version><configuration><descriptorRefs><descriptorRef>jar-with-dependencies</descriptorRef></descriptorRefs><archive><manifest><mainClass>Main.Main</mainClass></manifest></archive></configuration><executions><execution><id>make-assembly</id><phase>package</phase><goals><goal>single</goal></goals></execution></executions></plugin>
maven指令
mvn assembly:assembly

含有依賴方面所對應的jar包依賴的核心數據包

<build>  <plugins>  <plugin>  <groupId>org.apache.maven.plugins</groupId>  <artifactId>maven-assembly-plugin</artifactId>  <version>2.5.5</version>  <configuration>  <archive>  <manifest>  <mainClass>com.xxg.Main</mainClass>  </manifest>  </archive>  <descriptorRefs>  <descriptorRef>jar-with-dependencies</descriptorRef>  </descriptorRefs>  </configuration>  </plugin>  </plugins>  
</build>  

打包方式:

mvn package assembly:single  

打包后會在target目錄下生成一個xxx-jar-with-dependencies.jar文件,這個文件不但包含了自己項目中的代碼和資源,還包含了所有依賴包的內容。所以可以直接通過java -jar來運行。此外還可以直接通過mvn package來打包,無需assembly:single,不過需要加上一些配置:

<plugins>  <plugin>  <groupId>org.apache.maven.plugins</groupId>  <artifactId>maven-assembly-plugin</artifactId>  <version>2.5.5</version>  <configuration>  <archive>  <manifest>  <mainClass>com.xxg.Main</mainClass>  </manifest>  </archive>  <descriptorRefs>  <descriptorRef>jar-with-dependencies</descriptorRef>  </descriptorRefs>  </configuration>  <executions>  <execution>  <id>make-assembly</id>  <phase>package</phase>  <goals>  <goal>single</goal>  </goals>  </execution>  </executions>  </plugin>  </plugins>  
</build>  

其中package、single即表示在執行package打包時,執行assembly:single,所以可以直接使用mvn package打包。

不過,如果項目中用到spring Framework,用這種方式打出來的包運行時會出錯,使用下面的方法三可以處理。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/214113.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/214113.shtml
英文地址,請注明出處:http://en.pswp.cn/news/214113.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

mybatis多表映射-分步查詢

1、建庫建表 create database mybatis-example; use mybatis-example; create table t_book (bid varchar(20) primary key,bname varchar(20),stuid varchar(20) ); insert into t_book values(b001,Java,s001); insert into t_book values(b002,Python,s002); insert into …

C++大型項目經驗

1 附加包含目錄 在Visual Studio中&#xff0c;“附加包含目錄”&#xff08;Additional Include Directories&#xff09;是一個編譯器設置&#xff0c;它指示編譯器在查找包含文件&#xff08;通常是頭文件&#xff0c;擴展名為.h或.hpp&#xff09;時去哪些額外的文件夾路徑…

函數的棧幀

我們每次在調用函數的時候&#xff0c;都說會進行傳參。每次創建函數&#xff0c;或者進行遞歸的時候&#xff0c;也會說會進行壓棧。 那么&#xff0c;今天我們就來具體看看函數到底是如何進行壓棧&#xff0c;傳參的操作。 什么是棧&#xff1f; 首先我們要知道&#xff0c;…

Error opening file for writing報錯解決

報錯展示及描述 在安裝pycharm的時候出現了一下報錯&#xff0c; Error opening file for writing。 報錯原因 一般出現這種報錯都是文件權限的原因&#xff0c;檢查一下&#xff0c;果然這個文件夾權限是【只讀】 查看文件權限的方式&#xff1a;【右擊】文件夾名稱&#xff0…

CSS結構偽類選擇器之否定偽類:not()

結構偽類選擇器是針對 HTML 層級結構的偽類選擇器。 常用的結構化偽類選擇器有&#xff1a; :root選擇器、:not選擇器、:only-child選擇器、:first-child選擇器、:last-child選擇器、 :nth-child選擇器、:nth-child(n)選擇器、:nth-last-child(n)選擇器、:nth-of-type(n)選擇…

046:vue通過axios調用json地址數據的方法

第046個 查看專欄目錄: VUE ------ element UI 專欄目標 在vue和element UI聯合技術棧的操控下&#xff0c;本專欄提供行之有效的源代碼示例和信息點介紹&#xff0c;做到靈活運用。 &#xff08;1&#xff09;提供vue2的一些基本操作&#xff1a;安裝、引用&#xff0c;模板使…

Userwindows pc電腦生成一個電腦唯一機器碼

在Windows PC上生成一個唯一標識碼通常涉及到計算機硬件和軟件的信息。有一些常見的方式可以獲取到一個較為唯一的標識碼&#xff1a; 1. 硬件信息&#xff1a; 可以通過獲取計算機的硬件信息來生成一個唯一標識碼。這可能包括CPU序列號、硬盤序列號、網卡MAC地址等。但請注意…

ffmpeg過濾器filter理論與實戰

文章目錄 前言一、DirectShow1、簡介2、程序基本結構3、架構 二、過濾器1、視頻過濾器 -vf2、音頻過濾器 -af3、過濾器鏈&#xff08;Filterchain&#xff09;4、過濾器圖&#xff08;Filtergraph&#xff09;①、基本語法②、Filtergraph 的分類 5、結構體間的關系圖 三、過濾…

Vue項目中WebSocket封裝

WEBSOCKET 封裝引入初始化使用 封裝 utils下建立WebSocketManager.js class WebSocketManager {constructor() {this.url null;this.websocket null;this.isConnected false;this.listeners {onopen: [],onmessage: [],onclose: [],onerror: [],};this.reconnectionOptio…

QML如何與C++層進行信號槽通訊

//QML端為槽函數 //其中serial為C類的對象 //CSerial serial(暫且可以這么理解) QML&#xff1a; Connections{ target: serial onStringReceived:{ console.log("receive:"receiveString) } } //C端為信號 //C //C類…

kafka 常用命令【學習筆記】

Kafka 環境變量配置 export KAFKA_HOME/opt/cloudera/parcels/CDH-6.3.2-1.cdh6.3.2.p0.1605554/lib/kafka export PATH P A T H : PATH: PATH:KAFKA_HOME/bin 查看主題 ./kafka-topics.sh --list --zookeeper localhost:2181 創建主題 ./kafka-topics.sh --create --zook…

保研畢業論文查重率多少通過【保姆教程】

大家好&#xff0c;今天來聊聊保研畢業論文查重率多少通過&#xff0c;希望能給大家提供一點參考。 以下是針對論文重復率高的情況&#xff0c;提供一些修改建議和技巧&#xff1a; 保研畢業論文查重率多少通過 在保研過程中&#xff0c;畢業論文的查重率是衡量學術誠信和論文…

JAVA8新特性之函數式編程詳解

JAVA8新特性之函數式編程詳解 前言一、初步了解函數式接口二、 Lambda表達式2.1 概述2.2 lambda省略規則2.3 lambda省略常見實例2.4 lambda表達式與函數式接口 三、 Stream流3.1 stream流的定義3.2 Stream流的特點3.3 Stream流的三個步驟3.4 Stream 和 Collection 集合的區別&a…

【HarmonyOS開發】拖拽動畫的實現

動畫的原理是在一個時間段內&#xff0c;多次改變UI外觀&#xff0c;由于人眼會產生視覺暫留&#xff0c;所以最終看到的就是一個“連續”的動畫。UI的一次改變稱為一個動畫幀&#xff0c;對應一次屏幕刷新&#xff0c;而決定動畫流暢度的一個重要指標就是幀率FPS&#xff08;F…

【帶頭學C++】----- 九、類和對象 ---- 9.12 C++之友元函數(9.12.1---12.4)

??????????????????????創做不易&#xff0c;麻煩點個關注???????????????????????? ??????????????????文末有驚喜&#xff01;獻舞一支&#xff01;???????????????????? 目錄 9.12…

TypeError: Cannot set properties of undefined (setting ‘xx‘)

在寫代碼中經常會遇到TypeError: Cannot set properties of undefined (setting ‘xx‘)&#xff0c;這個問題。 一般出現的場景&#xff1a;在調用接口訪問后端數據時&#xff0c;前端渲染顯示空白&#xff0c;并報此錯。例如&#xff0c;我在調用高德地圖&#xff0c;輸入經…

五:爬蟲-數據解析之xpath解析

三&#xff1a;數據解析之xpath解析 1.xpath介紹&#xff1a; ? xpath是XML路徑語言&#xff0c;它可以用來確定xml文檔中的元素位置&#xff0c;通過元素路徑來完成對元素的查找&#xff0c;HTML就是XML的一種實現方式&#xff0c;所以xpath是一種非常強大的定位方式? XPa…

vue2 element-ui select下拉框 選擇傳遞多個參數

<el-select v-model"select" slot"prepend" placeholder"請選擇" change"searchPostFn($event,123)"> <el-option :label"item.ziDianShuJu" :value"{value:item.id, label:item.ziDianShuJu}" v-for&qu…

Ubuntu系統使用快速入門實踐(七)——軟件安裝與使用(5)

Ubuntu系統使用快速入門實踐系列文章 下面是Ubuntu系統使用系列文章的總鏈接&#xff0c;本人發表這個系列的文章鏈接均收錄于此 Ubuntu系統使用快速入門實踐系列文章總鏈接 下面是專欄地址&#xff1a; Ubuntu系統使用快速入門實踐系列文章專欄 文章目錄 Ubuntu系統使用快速…

crypto-js加密、解密與node Crypto加解密模塊的應用

前端用crypto-js實現加解密&#xff0c;node端用Crypto模塊&#xff0c;兩者想要相同結果的話&#xff0c;就要保持加密密鑰和加密算法一致。 crypto-js加密、解密 參考&#xff1a; 『crypto-js 加密和解密』 前端使用CryptoJS加密解密 // DES算法 import CryptoJS from cryp…