java项目打包(maven+原生)
一、maven打jar包
1.1 没有第三方依赖的jar
java -jar maven项目打包提示.jar中没有主清单属性
<build>
<finalName>${project.artifactId}</finalName>
<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>
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 把第三方依赖放入其他目录,自己的代码单独形成jar
需要两个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命令打jar包
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 .\target\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命令是打包,命令类似linux tar
{}参数(只能选一个)
参数 | 说明 |
---|---|
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包中目录错误
jar包的正确结构
xxx.jar
└—— com\leon\xxx.class…
└—— META-INF\MANIFEST.MF
jar包的错误结构
xxx.jar
└—— target\classes\com\leon\xxx.class…
└—— META-INF\MANIFEST.MF
解决办法一 切换目录法
cd进入classes中package目录的上一层,以示例来说,就是target\classes
然后,jar命令打包————目的就是要去掉jar中的target\classes这两层不必要的目录
然后,把jar放入到合适的位置运行java -jar(与第三方依赖的相对正确的位置)
解决办法二 -C参数(推荐)
jar cvfm Main.jar META-INF\MANIFEST.MF com\leon\Main.class -C target\classes .
-C 参数的含义就是要“剥去”jar包中的某些目录层级
注意
- 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格式有关