一、maven打包
1.1 普通jar
java -jar maven專案打包提示.jar中沒有主清單屬性
<build>
<finalName>${project.artifactId}</finalName><!--修改編譯出來的jar包名,僅為{artifactId}.jar-->
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.2.0</version>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<mainClass>com.leon.Main</mainClass> <!-- 此處為主入口-->
</manifest>
</archive>
</configuration>
</plugin>
</plugins>
</build>
原文連結:https://blog.csdn.net/whq12789/article/details/106568531
1.2 有依賴的jar
How can I create an executable/runnable JAR with dependencies using Maven?
1. 一個jar
最高贊
<build>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<archive>
<manifest>
<mainClass>fully.qualified.MainClass</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
<executions>
<execution>
<id>make-assembly</id> <!-- this is used for inheritance merges -->
<phase>package</phase> <!-- bind to the packaging phase -->
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
2 把依賴放入其他目錄
需要兩個plugin結合使用
注意
- outputDirectory,就是生成的可執行的jar的依賴的目錄
- maven-jar-plugin的classpathPrefix,作用是生產的可執行jar的MANIFEST.MF裡面的Class-Path
- 這個方法,並不用我手動copy lib到專案裡面,maven會自己從倉庫裡面copy
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy-dependencies</id>
<phase>prepare-package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}/lib</outputDirectory>
<overWriteReleases>false</overWriteReleases>
<overWriteSnapshots>false</overWriteSnapshots>
<overWriteIfNewer>true</overWriteIfNewer>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<classpathPrefix>lib/</classpathPrefix>
<mainClass>theMainClass</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
二、java命令打包
0、程式碼目錄結構
projectDir\
└—— src\com\leon\xxx.java....
└—— lib\xxx.jar
└—— META-INF\MANIFEST.MF
└—— target\classes
- 4個目錄的結構都不是死的,都可以改變,但是以上的結構比較符合一般的專案結構
- lib下放三方依賴
- META-INF,一般是hyphen而不是underline,別記錯寫錯。MANIFEST.MF可以寫成txt,只要在引數裡指定就好
1、javac編譯成classes
javac -encoding UTF-8 -classpath .\lib\commons-lang3-3.7.jar -d .\classes src\com\leon\Main.java
option引數 | 說明 |
---|---|
-encoding UTF-8 | java檔案中文編碼 |
-d .\target\classes | 將打包的class檔案輸出到指定目錄 |
-classpath .\lib\xxx.jar | 簡寫-cp 指定依賴的第三方jar 目錄以【.\】開頭或者省略【.\】似乎沒關係,並不會導致固定死目錄(包含本地目錄比如D盤) |
source file引數 | |
src\......java | 應該能簡寫成目錄 |
2、驗證classes的正確性
java -cp target\classes;lib\commons-lang3-3.7.jar com.leon.Main abc
- java執行class,首先需要指定自定義的classes的位置,-cp target\classes
- java命令的class引數要寫【package的路徑+class名字】,com.leon.Main
如下
這個路徑,沒有包含目錄src,src僅僅是一個目錄,它不在java檔案的package命令裡面
package com.leon;
public class Main {
}
- 注意 -cp命令沒有以【.\】開頭
可能的錯誤
- java.lang.NoClassDefFoundError: xxx第三方引用
-cp要指定第三方包,lib\commons-lang3-3.7.jar,多個第三方jar以semicolon分割
3、jar打包
95%內容參考自oracle的官方文件,Packaging Programs in JAR Files
jar的命令格式以jdk的幫助講解,如下
jar {ctxui}[vfmn0PMe] [jar-file] [manifest-file] [entry-point] [-C dir] files ...
jar命令是打包,命令很類似ta
{}引數(只能選一個)
引數 | 說明 |
---|---|
c | 打包成成為jar |
基本不用 | |
x | 解壓jar |
t | 顯示jar中的內容 |
u | 更新jar |
x或i | 沒試過 |
[]引數
重要引數 | 說明 |
---|---|
m | 指定MANIFEST檔案 |
f | 打包的jar的名字,這個引數不傳會output will go to a stdout(什麼人或者為啥要go to stdout?) |
e | 可以指定class的入口。 |
C | 去掉指定的目錄層級。看例項。 |
不重要引數 | |
v | 打包時,顯示詳細的資訊 |
0 | 不壓縮。不壓縮的jar據oracle網站說執行更快,壓縮體積更小 |
M | jar內不要MANIFEST,為啥不要? |
注意:
m與f引數的順序與[jar-file] [manifest-file]順序是必須對應的。
正確jar命令
jar cvfm Main.jar META-INF\MANIFEST.MF com\leon\Main.class -C target\classes .
MANIFEST.MF
- 自定義的MF檔案,每行都是鍵值對的形式,以colon分割
These lines show that a manifest's entries take the form of "header: value" pairs. - 自定義的MF檔案可以寫成txt,也可以.MF,檔名也可以任意,但是需要透過【m 引數+[manifest-file]】
- 自定義的MF檔案最後一行不會被寫入到jar包中的MANIFEST.MF中,所以自定義的MF檔案最後一行必須是空行
來源:
Modifying a Manifest File,Warning: The text file from which you are creating the manifest must end with a new line or carriage return. The last line will not be parsed properly if it does not end with a new line or carriage return. - MF檔案需要以UTF-8編碼
Main-Class
例
Main-Class: com.leon.Main
Class-Path
格式 Class-Path: jar1-name jar2-name directory-name/jar3-name
例
Class-Path: lib\commons-lang3-3.7.jar
- 在某個回答上看,不能以\開頭(linux是/)。沒驗證
- jar包之間以空格分割
- 我看別人打包有以【.】開頭的,如下,是為了把當前資料夾也包括進去?
Class-Path: . xxx1.jar xxx2.jar
jar打包正確之後,java -jar卻報錯“找不到或無法載入主類 xxx”
jar包的正確結構
xxx.jar
└—— com\leon\xxx.java....
└—— META-INF\MANIFEST.MF
如果出現以上錯誤,很可能是jar包中目錄錯誤,如下
xxx.jar
└—— target\classes\com\leon\xxx.java....
└—— META-INF\MANIFEST.MF
解決辦法一 切換目錄法
cd進入classes中package目錄的上一層,以示例來說,就是target\classes
然後,jar命令打包
然後,把jar放入到合適的位置執行java -jar(主要是與第三方依賴的相對位置要放置準確)
解決辦法二 -C引數(推薦)
jar cvfm Main.jar META-INF\MANIFEST.MF com\leon\Main.class -C target\classes .
注意
- com\leon\Main.class是位於target\classes中的(這裡與src目錄無關)
- -C引數最後有一個【空格+.】
參考,could not find or load main class with a jar file,中作者Log2的回答,他解釋的非常清楚,兩種解決辦法都有提到。
可能的錯誤
- 提示 xxx.jar中沒有主清單屬性,開啟jar
以下命令會在jar中生成預設MF,位置在xxx.jar\META-INF\MANIFEST.MF,但是裡面沒有Main-Class
需要用-m或-e引數指定jar -cvf Main.jar .\classes\com\leon\Main.class
- 有m選項,也自定義了MANIFEST.MF,但是jar中的MANIFEST.MF還是沒有Main-Class
看看是不是MF檔案只寫了一行,沒有在後面跟一個空行 - 報錯 java.io.IOException: invalid header field
前面oracle文件說了,"header: value" pairs,所以,這個報錯與MF格式有關