打包可运行 jar 包的几种方式

Last Modified: 2023/05/09

概述

我们知道 spring boot 项目可以打包成一个可直接运行的 fat jar,事实上除了 spring boot 项目,我们自建的 maven 项目也可以打包成可运行的 jar 包,本文将介绍几个 maven 插件,以及如何利用这些插件来打包可执行 jar 包。

可以利用下面的三个 maven 插件来打包可以执行 jar 包,他们分别是:

  • maven-jar-plugin
  • maven-assembly-plugin
  • maven-shade-plugin

maven-jar-plugin

该插件提供了构建 JAR 包的能力,但它只会编译位于 src/main/java 和 /src/main/resources/ 目录下的 Java 文件,不包括依赖的 JAR 文件。

为了创建可执行的 jar,我们先引入 maven-jar-plugin 插件,然后使用 mainClass 指定 main 方法所在类的全限定类名。下面的例子中,假设 main 方法所在的类为 net.verytools.prac.Main

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-jar-plugin</artifactId>
  <version>3.3.0</version>
  <configuration>
    <archive>
      <manifest>
        <mainClass>net.verytools.prac.Main</mainClass>
      </manifest>
    </archive>
  </configuration>
</plugin>

打包方法很简单,在 maven 项目根目录下执行 mvn package 即可。打包完成后通常在 target 目录下生成 jar 包,假设生成的 jar 包名称为 prac-execjar-1.0.jar,接下来运行 jar 包的命令如下:

java -jar prac-execjar-1.0.jar

前面提到了,该插件不会打包依赖的其他 jar,假如我们的项目依赖了 hutool,那么打包出来的 jar 不能直接运行,需要在 classpath 中包含 hutool 以及 hutool 本身依赖的所有 jar,才可以执行 prac-execjar-1.0.jar 中的 main 方法(假设 main 中使用到了 hutool 库中的方法)。

这个时候我们可以借助 maven-dependency-plugin 将本项目所有依赖的 jar 全部拷贝到一个指定的目录下以备使用,插件配置如下:

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-dependency-plugin</artifactId>
  <version>3.5.0</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>

通过以上配置,运行 mvn package 会将项目依赖的第三方 jar 全部拷贝到 target/lib 目录下。运行 jar 中的 main 方法有两种办法:

1、将所有的 jar 一个一个的列出来

java -cp target/lib:xx.jar:target/lib:yy.jar:prac-execjar-1.0.jar net.verytools.prac.Main

如果依赖的第三方 jar 包很多,那么这种操作简直令人窒息,好在还有第二种方案,即使用通配符

2、使用通配符

java -cp "target/lib/*:target/prac-execjar-1.0.jar" net.verytools.prac.Main

使用通配符的时候需要注意:target/lib/* 即可匹配 target/lib 目录下的所有 jar,千万不要画蛇添足使用 target/lib/*.jar;另外一点,必须使用双引号-cp 的参数值括起来,否则不生效。

注意:在 windows 中需要使用分号分隔 jar,另外 windows 上的文件路径使用反斜杠,linux 中则使用的是斜杠。

java -cp "D:\path\to\lib\*" net.verytools.prac.Main

还有一点需要注意,-cp-jar 这两个参数不能同时使用。

maven-assembly-plugin

使用该插件,可以将项目的所有内容以及第三方 jar 全都打包到一起成为一个 fat jar,只需要这一个 jar 便可以启动项目,是不是很 cool,配置方法如下:

<plugin>
  <artifactId>maven-assembly-plugin</artifactId>
  <version>3.3.0</version>
  <configuration>
    <archive>
      <manifest>
        <mainClass>net.verytools.prac.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>

运行 mvn package 打包项目,会在 target 目录下生成两个 jar:prac-execjar-1.0.jar 和 prac-execjar-1.0-jar-with-dependencies.jar, 第一个不包含第三方依赖,第二个是包含所有第三方依赖的 fat jar。只需要 fat jar 便可以启动项目:

java -jar prac-execjar-1.0-jar-with-dependencies.jar

该插件会将所有依赖的 JAR 包中的内容解压出来,并和我们项目的 class 文件放到一起。它只适用于依赖较少的项目,对于依赖较多的大型项目,可能会导致 Java 类名冲突问题。

maven-shade-plugin

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-shade-plugin</artifactId>
  <executions>
    <execution>
      <phase>package</phase>
      <goals>
        <goal>shade</goal>
      </goals>
      <configuration>
        <transformers>
          <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
            <mainClass>
              net.verytools.prac.Main
            </mainClass>
          </transformer>
        </transformers>
      </configuration>
    </execution>
  </executions>
</plugin>

该插件打包出来的 fat jar 又称之为 uber jar。该插件可以通过重定位类来解决冲突,另外当 JAR 包中存在具有相同名称的资源文件时,该插件还能合并资源文件。具体用法查看插件文档,以上配置展示了如何使用它来打包一个可执行的 uber jar。

ManifestResourceTransformer 用来修改 jar 中 MANIFEST.MF 文件,添加 mainClass,这样打出来才是可执行的 jar。

有问题吗?点此反馈!

温馨提示:反馈需要登录