题 如何使用Maven创建具有依赖关系的可执行JAR?


我想将我的项目打包在一个可执行的JAR中进行分发。

如何将所有依赖JAR的Maven项目打包到我的输出JAR中?


1944
2018-02-22 08:43


起源


请解释您所指的依赖插件的目标。我知道没有做出原始问题请求的目标:将所有依赖项A)放入作者jar中通过重新打包,或者B)创建一个可执行jar,其他人在MANIFEST.MF的类路径中 - Matthew McCullough
你可能会觉得这很有用 rationaljava.com/2015/02/... - Dan
2个例子: tugay.biz/2015/12/a-standalone-java-web-application-with.html  tugay.biz/2016/11/how-did-i-create-executable-jar-with.html - Koray Tugay
参考 stackoverflow.com/questions/35217128/... - Thanga


答案:


<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>
    </plugin>
  </plugins>
</build>

你运行它

mvn clean compile assembly:single

应该在汇编之前添加编译目标:单个或者不包括您自己项目中的代码。

在评论中查看更多详情。


通常,此目标与要自动执行的构建阶段相关联。这可确保在执行时构建JAR mvn install 或执行部署/发布。

<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>

1866
2017-12-01 10:46



谢谢@IAdapter。请注意,您应该事先进行编译,因为它只会将JAR中的“target / classes”中的内容放入其中。这将确保JAR包含您最近对源代码所做的任何更改。所以,你应该做的事情如下: mvn clean compile assembly:single。 - Michael
我编辑了这个问题,包括相位绑定。我删除了已弃用的程序集目标,因为没有人需要知道这一点。 - Duncan Jones
我看到这不会将jar添加到uber jar中,而只是将所有类文件添加到jar中。 - pitchblack408
提示:您还可以添加元素 <appendAssemblyId>false</appendAssemblyId> 进入 configuration 避免名称中带有恼人的“-jar-with-dependencies”后缀 - maxivis
忘记 compile 你被搞砸了 - prayagupd


您可以使用dependency-plugin在包阶段之前在单独的目录中生成所有依赖项,然后将其包含在清单的类路径中:

<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>

或者使用 ${project.build.directory}/classes/lib 作为OutputDirectory将所有jar文件集成到主jar中,但是你需要添加自定义类加载代码来加载jar。


294
2018-06-02 03:01



+1优秀。我使用maven-dependency-plugin而不是maven-assembly-plugin的原因是我也使用buildnumber-maven-plugin,这样我就可以将版本号分别存储在每个jar的清单中。 - PapaFreud
我喜欢你的解决方案。我用 ${project.build.directory}/classes/lib 如 outputDirectory 有一个主要的.jar里面有所有依赖项,但是 - 如何添加自定义类加载代码来加载这个jar?我需要让工作执行如下: java -jar main-jar-with-deps.jar。这可能吗 ? - marioosh
@AndréAronsen,我用这个解决方案在jar里面的lib文件夹中添加依赖项,但是我总是得到类找不到异常,请你告诉我如何修复它。 - Mahmoud Saleh
给你+1!看起来像maven程序集插件'jar-with-dependencies'并不能很好地工作。我在生成的jar中缺少META-INF / spring.schemas中的一些条目。所以我废弃了依赖jar的jar并使用了上面的解决方案。很好,谢谢!!! - Derek
对于遇到此问题的任何其他人,您必须将jar文件夹包含在与jar一起运送jar的同一目录中。 - Sparticles


我在博客上写了一些不同的方法来做到这一点。

看到 Apache Maven的可执行jar (WordPress的)

要么 可执行-罐与 - 行家-示例 (GitHub的)

笔记

这些优点和缺点由提供 斯蒂芬


用于手动部署

  • 优点
  • 缺点
    • 依赖性超出了最后的jar。

将依赖项复制到特定目录

<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}/${project.build.finalName}.lib</outputDirectory>
      </configuration>
    </execution>
  </executions>
</plugin>

使Jar可执行文件和类路径感知

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-jar-plugin</artifactId>
  <configuration>
    <archive>
      <manifest>
        <addClasspath>true</addClasspath>
        <classpathPrefix>${project.build.finalName}.lib/</classpathPrefix>
        <mainClass>${fully.qualified.main.class}</mainClass>
      </manifest>
    </archive>
  </configuration>
</plugin>

此时此刻 jar 实际上可以使用外部类路径元素执行。

$ java -jar target/${project.build.finalName}.jar

制作可部署的档案

jar 文件只能与兄弟一起执行 ...lib/ 目录。我们需要使用目录及其内容来部署存档。

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-antrun-plugin</artifactId>
  <executions>
    <execution>
      <id>antrun-archive</id>
      <phase>package</phase>
      <goals>
        <goal>run</goal>
      </goals>
      <configuration>
        <target>
          <property name="final.name" value="${project.build.directory}/${project.build.finalName}"/>
          <property name="archive.includes" value="${project.build.finalName}.${project.packaging} ${project.build.finalName}.lib/*"/>
          <property name="tar.destfile" value="${final.name}.tar"/>
          <zip basedir="${project.build.directory}" destfile="${final.name}.zip" includes="${archive.includes}" />
          <tar basedir="${project.build.directory}" destfile="${tar.destfile}" includes="${archive.includes}" />
          <gzip src="${tar.destfile}" destfile="${tar.destfile}.gz" />
          <bzip2 src="${tar.destfile}" destfile="${tar.destfile}.bz2" />
        </target>
      </configuration>
    </execution>
  </executions>
</plugin>

现在你有 target/${project.build.finalName}.(zip|tar|tar.bz2|tar.gz) 每个都包含 jar 和 lib/*


Apache Maven Assembly插件

  • 优点
  • 缺点
    • 没有类重定位支持(如果需要类重定位,请使用maven-shade-plugin)。
<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-assembly-plugin</artifactId>
  <executions>
    <execution>
      <phase>package</phase>
      <goals>
        <goal>single</goal>
      </goals>
      <configuration>
        <archive>
          <manifest>
            <mainClass>${fully.qualified.main.class}</mainClass>
          </manifest>
        </archive>
        <descriptorRefs>
          <descriptorRef>jar-with-dependencies</descriptorRef>
        </descriptorRefs>
      </configuration>
    </execution>
  </executions>
</plugin>

你有 target/${project.bulid.finalName}-jar-with-dependencies.jar


Apache Maven Shade插件

  • 优点
  • 缺点
<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-shade-plugin</artifactId>
  <executions>
    <execution>
      <goals>
        <goal>shade</goal>
      </goals>
      <configuration>
        <shadedArtifactAttached>true</shadedArtifactAttached>
        <transformers>
          <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
            <mainClass>${fully.qualified.main.class}</mainClass>
          </transformer>
        </transformers>
      </configuration>
    </execution>
  </executions>
</plugin>

你有 target/${project.build.finalName}-shaded.jar


onejar - Maven的插件

  • 优点
  • 缺点
    • 自2012年以来未得到积极支持
<plugin>
  <!--groupId>org.dstovall</groupId--> <!-- not available on the central -->
  <groupId>com.jolira</groupId>
  <artifactId>onejar-maven-plugin</artifactId>
  <executions>
    <execution>
      <configuration>
        <mainClass>${fully.qualified.main.class}</mainClass>
        <attachToBuild>true</attachToBuild>
        <!-- https://code.google.com/p/onejar-maven-plugin/issues/detail?id=8 -->
        <!--classifier>onejar</classifier-->
        <filename>${project.build.finalName}-onejar.${project.packaging}</filename>
      </configuration>
      <goals>
        <goal>one-jar</goal>
      </goals>
    </execution>
  </executions>
</plugin>

Spring Boot Maven插件

  • 优点
  • 缺点
    • 添加潜在的不必要的Spring和Spring Boot相关类。
<plugin>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-maven-plugin</artifactId>
  <executions>
    <execution>
      <goals>
        <goal>repackage</goal>
      </goals>
      <configuration>
        <classifier>spring-boot</classifier>
        <mainClass>${fully.qualified.main.class}</mainClass>
      </configuration>
    </execution>
  </executions>
</plugin>

你有 target/${project.bulid.finalName}-spring-boot.jar


134
2018-02-26 04:31



唯一适合我的是手动部署。 - caiohamamura
@caiohamamura你可以克隆 GitHub存储库 并查看所有配置文件的工作原理 - Jin Kwon
问题在于我使用的包: stackoverflow.com/a/12622037/2548351 - caiohamamura


采取未答复的答案并重新格式化,我们有:

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-jar-plugin</artifactId>
            <configuration>
                <archive>
                    <manifest>
                        <addClasspath>true</addClasspath>
                        <mainClass>fully.qualified.MainClass</mainClass>
                    </manifest>
                </archive>
            </configuration>
        </plugin>
        <plugin>
            <artifactId>maven-assembly-plugin</artifactId>
            <configuration>
                <descriptorRefs>
                    <descriptorRef>jar-with-dependencies</descriptorRef>
                </descriptorRefs>
            </configuration>
        </plugin>
    </plugins>
</build>

接下来,我建议将其作为构建的一部分,而不是明确调用。要使其成为您的构建中不可或缺的一部分,请将此插件添加到您的 pom.xml 并将其绑定到 package 生命周期事件。然而,问题是你需要打电话给 assembly:single 目标,如果将它放在你的pom.xml中,你可以调用'assembly:assembly',如果从命令行手动执行它。

<project>
  [...]
  <build>
      <plugins>
          <plugin>
              <artifactId>maven-assembly-plugin</artifactId>
              <configuration>
                  <archive>
                      <manifest>
                          <addClasspath>true</addClasspath>
                          <mainClass>fully.qualified.MainClass</mainClass>
                      </manifest>
                  </archive>
                  <descriptorRefs>
                      <descriptorRef>jar-with-dependencies</descriptorRef>
                  </descriptorRefs>
              </configuration>
              <executions>
                  <execution>
                      <id>make-my-jar-with-dependencies</id>
                      <phase>package</phase>
                      <goals>
                          <goal>single</goal>
                      </goals>
                  </execution>
              </executions>
          </plugin>
      [...]
      </plugins>
    [...]
  </build>
</project>

122
2017-09-22 15:20



使用此答案中的方法会导致以下错误消息:'尝试使用'java -jar <jar file>'运行JAR时,无法从<jar file>'加载Main-Class清单属性 - Elmo
需要存档部分maven-jar-plugin <archive> <manifest> <addClasspath> true </ addClasspath> <mainClass> fully.qualified.MainClass </ mainClass> </ manifest> </ archive> - RockyMM
对不起,这个答案是完全错误的,mainClass标签必须在maven-assembly-plugin条目上,因为你在包目标期间调用它 - Alex Lehmann
@AlexLehmann真的! - RockyMM
我很惊讶,为什么不能在mvn archetype:generate命令之后将pom.xml包含在内?每次创建新的maven项目时手动复制粘贴都有点烦人... - wintermute


使用maven-shade-plugin将所有依赖项打包到一个超级jar中。它还可以通过指定主类来构建可执行jar。在尝试使用maven-assembly和maven-jar之后,我发现这个插件最适合我的需求。

我发现这个插件特别有用,因为它合并了特定文件的内容而不是覆盖它们。如果资源文件在jar中具有相同的名称,并且插件尝试打包所有资源文件,则需要这样做

见下面的例子

      <plugins>
    <!-- This plugin provides the capability to package the artifact in an uber-jar, including its dependencies and to shade - i.e. rename - the packages of some of the dependencies. -->
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-shade-plugin</artifactId>
            <version>1.4</version>
            <executions>
                <execution>
                    <phase>package</phase>
                    <goals>
                        <goal>shade</goal>
                    </goals>
                    <configuration>
                        <artifactSet>
                        <!-- signed jars-->
                            <excludes>
                                <exclude>bouncycastle:bcprov-jdk15</exclude>
                            </excludes>
                        </artifactSet>

                         <transformers>
                            <transformer
                                implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                <!-- Main class -->
                                <mainClass>com.main.MyMainClass</mainClass>
                            </transformer>
                            <!-- Use resource transformers to prevent file overwrites -->
                            <transformer 
                                 implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                                <resource>properties.properties</resource>
                            </transformer>
                            <transformer
                                implementation="org.apache.maven.plugins.shade.resource.XmlAppendingTransformer">
                                <resource>applicationContext.xml</resource>
                            </transformer>
                            <transformer
                                implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                                <resource>META-INF/cxf/cxf.extension</resource>
                            </transformer>
                            <transformer
                                implementation="org.apache.maven.plugins.shade.resource.XmlAppendingTransformer">
                                <resource>META-INF/cxf/bus-extensions.xml</resource>
                            </transformer>
                     </transformers>
                    </configuration>
                </execution>
            </executions>
        </plugin>

    </plugins>

91
2018-03-13 20:55



那么bcprov-jdk15.jar如何在运行时进入类路径,因为它被从着色过程中排除? - Andrew Swan
它被cxf-rt-ws-security所取代,这是我的依赖项的一部分 - Vijay Katam
之前从未听说过这个插件,但它解决了我在jar中使用spring.handlers的问题。谢谢! - Alexandre L Telles
那些获得安全例外的人将DSA排除在Manifest之外。检查 maven.apache.org/plugins/maven-shade-plugin/examples/... - ruhsuzbaykus
+1我过去曾使用过minijar:ueberjar,但minijar插件现已弃用并被阴影取代 - rds


长期使用了 maven汇编插件,但我无法找到问题的解决方案 "already added, skipping"。现在,我正在使用另一个插件 - onejar - Maven的插件。以下示例(mvn package 构建jar):

<plugin>
    <groupId>org.dstovall</groupId>
    <artifactId>onejar-maven-plugin</artifactId>
    <version>1.3.0</version>
    <executions>
        <execution>
            <configuration>
                <mainClass>com.company.MainClass</mainClass>
            </configuration>
            <goals>
                <goal>one-jar</goal>
            </goals>
        </execution>
    </executions>
</plugin>

您需要为该插件添加存储库:

<pluginRepositories>
    <pluginRepository>
        <id>onejar-maven-plugin.googlecode.com</id>
        <url>http://onejar-maven-plugin.googlecode.com/svn/mavenrepo</url>
    </pluginRepository>
</pluginRepositories>

15
2017-10-13 12:16



如何摆脱输出中的额外消息? - Alexandr


您可以使用maven-dependency-plugin,但问题是如何创建可执行JAR。要做到这一点,需要对Matthew Franglen的响应进行以下更改(顺便说一句,使用依赖插件从清理目标开始时需要更长时间才能构建):

<build>
    <plugins>
        <plugin>
            <artifactId>maven-jar-plugin</artifactId>
            <configuration>
                <archive>
                    <manifest>
                        <mainClass>fully.qualified.MainClass</mainClass>
                    </manifest>
                </archive>
            </configuration>
        </plugin>
        <plugin>
            <artifactId>maven-dependency-plugin</artifactId>
            <executions>
                <execution>
                    <id>unpack-dependencies</id>
                    <phase>package</phase>
                    <goals>
                        <goal>unpack-dependencies</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
    <resources>
        <resource>
            <directory>${basedir}/target/dependency</directory>
        </resource>
    </resources>
</build>

14
2018-03-11 15:12





如果你真的想要在你的单个结果JAR中重新打包其他JAR内容,那么另一种选择就是 Maven Assembly插件。它解压缩然后将所有内容重新打包到目录中 <unpack>true</unpack>。然后你会有第二遍将它构建成一个巨大的JAR。

另一种选择是OneJar插件。这将一步完成上述重新打包操作。


13
2017-08-13 18:23