406

有没有办法强制 maven(2.0.9) 将所有依赖项包含在一个 jar 文件中?

我有一个项目构建成一个 jar 文件。我也希望将依赖项中的类复制到 jar 中。

更新:我知道我不能只在 jar 文件中包含一个 jar 文件。我正在寻找一种方法来解压缩指定为依赖项的 jar,并将类文件打包到我的 jar 中。

4

15 回答 15

542

您可以使用带有“jar-with-dependencies”描述符的 maven-assembly 插件来执行此操作。这是我们的 pom.xml 之一的相关块:

  <build>
    <plugins>
      <!-- any other plugins -->
      <plugin>
        <artifactId>maven-assembly-plugin</artifactId>
        <executions>
          <execution>
            <phase>package</phase>
            <goals>
              <goal>single</goal>
            </goals>
          </execution>
        </executions>
        <configuration>
          <descriptorRefs>
            <descriptorRef>jar-with-dependencies</descriptorRef>
          </descriptorRefs>
        </configuration>
      </plugin>
    </plugins>
  </build>
于 2009-11-13T13:01:55.547 回答
165

使用 Maven 2,正确的方法是使用Maven2 程序集插件,它为此目的有一个预定义的描述符文件,您可以在命令行上使用它:

mvn assembly:assembly -DdescriptorId=jar-with-dependencies

如果你想让这个 jar 可执行,只需将要运行的主类添加到插件配置中:

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-assembly-plugin</artifactId>
  <configuration>
    <archive>
      <manifest>
        <mainClass>my.package.to.my.MainClass</mainClass>
      </manifest>
    </archive>
  </configuration>
</plugin>

如果您想创建该程序集作为正常构建过程的一部分,您应该将单个目录单个目标(该assembly目标只能从命令行运行)绑定到生命周期阶段(package有意义),如下所示:

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-assembly-plugin</artifactId>
  <executions>
    <execution>
      <id>create-my-bundle</id>
      <phase>package</phase>
      <goals>
        <goal>single</goal>
      </goals>
      <configuration>
        <descriptorRefs>
          <descriptorRef>jar-with-dependencies</descriptorRef>
        </descriptorRefs>
        ...
      </configuration>
    </execution>
  </executions>
</plugin>

调整configuration元素以满足您的需求(例如,使用清单内容)。

于 2009-11-13T13:29:05.690 回答
47

如果你想做一个可执行的 jar 文件,他们也需要设置主类。所以完整的配置应该是。

    <plugins>
            <plugin>
                 <artifactId>maven-assembly-plugin</artifactId>
                 <executions>
                     <execution>
                          <phase>package</phase>
                          <goals>
                              <goal>single</goal>
                          </goals>
                      </execution>
                  </executions>
                  <configuration>
                       <!-- ... -->
                       <archive>
                           <manifest>
                                 <mainClass>fully.qualified.MainClass</mainClass>
                           </manifest>
                       </archive>
                       <descriptorRefs>
                           <descriptorRef>jar-with-dependencies</descriptorRef>
                      </descriptorRefs>
                 </configuration>
         </plugin>
   </plugins>
于 2016-12-29T16:55:03.683 回答
20

shade maven 插件。它可用于打包和重命名依赖项(以省略类路径上的依赖问题)。

于 2009-11-13T13:02:03.063 回答
18

您可以使用<classifier>标签来使用新创建的 jar。

<dependencies>
    <dependency>
        <groupId>your.group.id</groupId>
        <artifactId>your.artifact.id</artifactId>
        <version>1.0</version>
        <type>jar</type>
        <classifier>jar-with-dependencies</classifier>
    </dependency>
</dependencies>
于 2012-01-18T19:10:25.873 回答
16
        <!-- Method 1 -->
        <!-- Copy dependency libraries jar files to a separated LIB folder -->
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-dependency-plugin</artifactId>
            <configuration>
                <outputDirectory>${project.build.directory}/lib</outputDirectory>
                <excludeTransitive>false</excludeTransitive> 
                <stripVersion>false</stripVersion>
            </configuration>
            <executions>
                <execution>
                    <id>copy-dependencies</id>
                    <phase>package</phase>
                    <goals>
                        <goal>copy-dependencies</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
        <!-- Add LIB folder to classPath -->
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-jar-plugin</artifactId>
            <version>2.4</version>
            <configuration>
                <archive>
                    <manifest>
                        <addClasspath>true</addClasspath>
                        <classpathPrefix>lib/</classpathPrefix>
                    </manifest>
                </archive>
            </configuration>
        </plugin>


        <!-- Method 2 -->
        <!-- Package all libraries classes into one runnable jar -->
        <plugin>
            <artifactId>maven-assembly-plugin</artifactId>
            <executions>
              <execution>
                <phase>package</phase>
                <goals>
                  <goal>single</goal>
                </goals>
              </execution>
            </executions>
            <configuration>
              <descriptorRefs>
                <descriptorRef>jar-with-dependencies</descriptorRef>
              </descriptorRefs>
            </configuration>
        </plugin>            
于 2018-03-21T05:39:45.557 回答
14

如果您(像我一样)不特别喜欢上面描述的jar-with-dependencies方法,我更喜欢的 maven 解决方案是简单地构建一个 WAR 项目,即使它只是您正在构建的独立 java 应用程序:

  1. 制作一个普通的 maven jar 项目,它将构建您的 jar 文件(没有依赖项)。

  2. 此外,设置一个 maven 战争项目(只有一个空的src/main/webapp/WEB-INF/web.xml文件,这将避免 maven-build 中的警告/错误),只有你的 jar 项目作为一个依赖项,并使您的 jar-project 成为<module>您的 war-project 下的一个。(这个战争项目只是将所有 jar 文件依赖项包装到 zip 文件中的一个简单技巧。)

  3. 构建战争项目以生成战争文件。

  4. 在部署步骤中,只需将 .war 文件重命名为 *.zip 并解压缩即可。

您现在应该有一个 lib 目录(您可以将其移动到您想要的位置),其中包含您的 jar 以及运行应用程序所需的所有依赖项:

java -cp 'path/lib/*' MainClass

(类路径中的通配符适用于 Java-6 或更高版本)

我认为这既更容易在 maven 中设置(无需弄乱组装插件),也让您更清楚地了解应用程序结构(您将在普通视图中看到所有依赖 jar 的版本号,并且避免将所有内容都塞进一个 jar 文件中)。

于 2013-05-16T15:21:33.190 回答
13

http://fiji.sc/Uber-JAR对替代方案提供了很好的解释:

构建 uber-JAR 的常用方法有以下三种:

  1. 无阴影。解压缩所有 JAR 文件,然后将它们重新打包到一个 JAR 中。
    • Pro:使用 Java 的默认类加载器。
    • 缺点:存在于具有相同路径的多个 JAR 文件(例如 META-INF/services/javax.script.ScriptEngineFactory)中的文件将相互覆盖,从而导致错误行为。
    • 工具:Maven 组装插件、Classworlds Uberjar
  2. 阴影。与无阴影相同,但重命名(即“阴影”)所有依赖项的所有包。
    • Pro:使用 Java 的默认类加载器。避免一些(不是全部)依赖版本冲突。
    • 缺点:存在于具有相同路径的多个 JAR 文件(例如 META-INF/services/javax.script.ScriptEngineFactory)中的文件将相互覆盖,从而导致错误行为。
    • 工具:Maven Shade 插件
  3. JAR 的 JAR。最终的 JAR 文件包含嵌入其中的其他 JAR 文件。
    • 优点:避免依赖版本冲突。所有资源文件都被保留。
    • 缺点:需要捆绑一个特殊的“引导”类加载器以使 Java 能够从包装的 JAR 文件中加载类。调试类加载器问题变得更加复杂。
    • 工具:Eclipse JAR 文件导出器,One-JAR。
于 2015-08-31T16:46:39.273 回答
4

我在 Eclipse Luna 和 m2eclipse 上的最终解决方案:自定义类加载器(下载并添加到您的项目,仅 5 个类): http: //git.eclipse.org/c/jdt/eclipse.jdt.ui.git/plain/org。 eclipse.jdt.ui/jar%20in%20jar%20loader/org/eclipse/jdt/internal/jarinjarloader/;这个类加载器是最好的一罐类加载器,而且速度非常快;

<project.mainClass>org.eclipse.jdt.internal.jarinjarloader.JarRsrcLoader</project.mainClass> <project.realMainClass>my.Class</project.realMainClass>

在 JIJConstants 中编辑“Rsrc-Class-Path”到“Class-Path”
mvn clean dependency:copy-dependencies 包
在 lib 文件夹中创建一个带有依赖项的 jar,并带有一个瘦类加载器

<build>
    <resources>
        <resource>
            <directory>src/main/java</directory>
            <includes>
                <include>**/*.java</include>
                <include>**/*.properties</include>
            </includes>
        </resource>
        <resource>
            <directory>src/main/resources</directory>
            <filtering>true</filtering>
            <includes>
                <include>**/*</include>
            </includes>
            <targetPath>META-INF/</targetPath>
        </resource>
        <resource>
            <directory>${project.build.directory}/dependency/</directory>
            <includes>
                <include>*.jar</include>
            </includes>
            <targetPath>lib/</targetPath>
        </resource>
    </resources>
<pluginManagement>
        <plugins>

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

                        <manifestEntries>
                            <Rsrc-Main-Class>${project.realMainClass}  </Rsrc-Main-Class>
                            <Class-Path>./</Class-Path>
                        </manifestEntries>

                    </archive>
                </configuration>
            </plugin>
<plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <executions>
                    <execution>
                        <id>copy-dependencies</id>
                        <phase>package</phase>
                        <goals>
                            <goal>copy-dependencies</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </pluginManagement>
</build>
于 2015-09-10T22:01:52.820 回答
1

抛开 Maven,您可以将 JAR 库放在 Main Jar 中,但您需要使用自己的类加载器。

检查这个项目:One-JAR链接文本

于 2009-11-13T13:56:40.803 回答
1

谢谢我在 POM.xml 文件中添加了下面的片段,并解决了 Mp 问题并创建了包含所有依赖 jar 的胖 jar 文件。

<plugin>
    <artifactId>maven-assembly-plugin</artifactId>
        <executions>
            <execution>
                <phase>package</phase>
                <goals>
                    <goal>single</goal>
                </goals>
            </execution>
        </executions>
        <configuration>
            <descriptorRefs>
                <descriptorRef>dependencies</descriptorRef>
            </descriptorRefs>
        </configuration>
    </plugin>
</plugins>
于 2016-10-17T09:17:22.010 回答
1

我试图做类似的事情,但我不希望所有的罐子都包括在内。我想从给定的依赖项中包含一些特定的目录。另外分类器标签已经被占用,所以我不能这样做:

<dependencies>
    <dependency>
        <groupId>your.group.id</groupId>
        <artifactId>your.artifact.id</artifactId>
        <version>1.0</version>
        <type>jar</type>
        <classifier>jar-with-dependencies</classifier>
    </dependency>
</dependencies>
  1. 我使用了 maven-dependency-plugin 和 unpack 目标
  2. 并将我想要的内容解压到${project.build.directory}/classes,否则将被省略
  3. 因为是在classes目录下,所以maven最后把它放到了jar里
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-dependency-plugin</artifactId>
    <executions>
        <execution>
            <id>unpack</id>
            <phase>prepare-package</phase>
            <goals>
                <goal>unpack</goal>
            </goals>
            <configuration>
                <artifactItems>
                    <artifactItem>
                        <groupId>my.group</groupId>
                        <artifactId>my.artifact</artifactId>
                        <classifier>occupied</classifier>
                        <version>1.0</version>
                        <type>jar</type>
                    </artifactItem>
                </artifactItems>
                <outputDirectory>${project.build.directory}/classes</outputDirectory>
                <includes>aaa/**, bbb/**, ccc/**</includes>
            </configuration>
        </execution>
    </executions>
</plugin>
于 2021-03-10T15:42:23.133 回答
0

这篇文章可能有点旧,但我最近也遇到了同样的问题。John Stauffer 提出的第一个解决方案是一个很好的解决方案,但是我在今年春天工作时遇到了一些问题。我使用的 spring 的依赖罐有一些属性文件和 xml-schemas 声明,它们共享相同的路径和名称。尽管这些 jars 来自相同的版本,但jar-with-dependencies maven-goal 正在用找到的最后一个文件覆盖这些文件。

最后,应用程序无法启动,因为 spring jar 找不到正确的属性文件。在这种情况下,Rop 提出的解决方案解决了我的问题。

同样从那时起,spring-boot 项目现在就存在了。它有一个非常酷的方法来管理这个问题,它提供了一个 maven 目标,它重载了包目标并提供了自己的类加载器。参见spring-boots 参考指南

于 2014-11-08T18:18:55.187 回答
0

看看这个答案:

我正在创建一个作为 Java JAR 文件运行的安装程序,它需要将 WAR 和 JAR 文件解压缩到安装目录中的适当位置。依赖插件可以在打包阶段与复制目标一起使用,它将下载 Maven 存储库中的任何文件(包括 WAR 文件)并将它们写入您需要的地方。我将输出目录更改为 ${project.build.directory}/classes,然后最终结果是正常的 JAR 任务包含我的文件就好了。然后我可以提取它们并将它们写入安装目录。

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
    <execution>
        <id>getWar</id>
        <phase>package</phase>
        <goals>
            <goal>copy</goal>
        </goals>
        <configuration>
            <artifactItems>
                <artifactItem>
                    <groupId>the.group.I.use</groupId>
                    <artifactId>MyServerServer</artifactId>
                    <version>${env.JAVA_SERVER_REL_VER}</version>
                    <type>war</type>
                    <destFileName>myWar.war</destFileName>
                </artifactItem>
            </artifactItems>
            <outputDirectory>${project.build.directory}/classes</outputDirectory>
        </configuration>
    </execution>
</executions>

于 2016-06-17T20:50:17.253 回答
-1

为了使其更简单,您可以使用以下插件。

             <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>
                                com.nirav.certificate.CertificateUtility
                            </mainClass>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
于 2021-02-09T07:34:10.080 回答