java项目打包(maven+原生)

一、maven打jar包

1.1 没有第三方依赖的jar

java -jar maven项目打包提示.jar中没有主清单属性

xml 复制代码
    <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

xml 复制代码
<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结合使用

注意

  1. outputDirectory,就是生成的可运行的jar的依赖的目录
  2. maven-jar-plugin的classpathPrefix,作用是生产的可运行jar的MANIFEST.MF里面的Class-Path
  3. 这个方法,并不用我手动copy lib到项目里面,maven会自己从仓库里面copy
xml 复制代码
<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

shell 复制代码
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的正确性

shell 复制代码
java -cp target\classes;lib\commons-lang3-3.7.jar com.leon.Main abc
  1. java运行class,首先需要指定自定义的classes的位置,-cp target\classes
  2. java命令的class参数要写【package的路径+class名字】,com.leon.Main
    如下
    这个路径,没有包含目录src,src仅仅是一个目录,它不在java文件的package命令里面
java 复制代码
package com.leon;
	public class Main {
	}
  1. 注意 -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的帮助讲解,如下

shell 复制代码
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命令

shell 复制代码
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 FileWarning: 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

  1. 在某个回答上看,不能以\开头(linux是/)。没验证
  2. jar包之间以空格分割
  3. 我看别人打包有以【.】开头的,如下,是为了把当前文件夹也包括进去?
MF 复制代码
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参数(推荐)
shell 复制代码
jar cvfm Main.jar META-INF\MANIFEST.MF com\leon\Main.class -C target\classes .

-C 参数的含义就是要"剥去"jar包中的某些目录层级
注意

  1. com\leon\Main.class是位于target\classes中的(这里与src目录无关)
  2. -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参数指定

    shell 复制代码
    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格式有关

相关推荐
Abladol-aj44 分钟前
并发和并行的基础知识
java·linux·windows
清水白石00844 分钟前
从一个“支付状态不一致“的bug,看大型分布式系统的“隐藏杀机“
java·数据库·bug
吾日三省吾码6 小时前
JVM 性能调优
java
弗拉唐7 小时前
springBoot,mp,ssm整合案例
java·spring boot·mybatis
oi778 小时前
使用itextpdf进行pdf模版填充中文文本时部分字不显示问题
java·服务器
少说多做3438 小时前
Android 不同情况下使用 runOnUiThread
android·java
知兀8 小时前
Java的方法、基本和引用数据类型
java·笔记·黑马程序员
蓝黑20208 小时前
IntelliJ IDEA常用快捷键
java·ide·intellij-idea
Ysjt | 深8 小时前
C++多线程编程入门教程(优质版)
java·开发语言·jvm·c++