java-maven-idea: 在 jar 中包含外部库

java-maven-idea: including external library in jar

我正在制作电报机器人,我需要 .jar 将其部署到云端。
我正在使用 intellij idea 中的 maven 构建它,但是当尝试在我的机器上执行时它会抛出这个:

Exception in thread "main" java.lang.NoClassDefFoundError: org/telegram/telegrambots/bots/TelegramLongPollingBot<br>

据我了解,发生这种情况是因为 Maven 没有将此库打包到 .jar 中。
我该怎么做?

粗略地说,你有两个选择

  1. 制作一个包含所有必需 类 的“胖”JAR
  2. 制作一个引用其他 JAR 文件的“瘦”JAR

最适合您的情况只有您才能决定。操作方法如下:

制作一个包含所有必需 类 的“胖”JAR

要遵循此方法,请使用 Maven Shade Plugin. In the package phase, you would invoke its shade goal。这会将 类 从您的依赖项以及您的应用程序 类 一起复制到一个 JAR-file 中。它在 POM 中可能看起来像这样:

<executions>
  <execution>
    <goals>
      <goal>shade</goal>
    </goals>
    <configuration>
      <finalName>my-packaged-application</finalName>
      <transformers>
        <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
          <mainClass>com.mycompany.MyApplication</mainClass>
        </transformer>
      </transformers>
      <filters>
        <filter>
          <!--
            Shading signed JARs will fail without this.
            
          -->
          <artifact>*:*</artifact>
            <excludes>
              <exclude>META-INF/*.SF</exclude>
              <exclude>META-INF/*.DSA</exclude>
              <exclude>META-INF/*.RSA</exclude>
            </excludes>
          </filter>
        </filters>
      </configuration>
    </execution>
</executions>

这种方法的优点是您的应用程序被打包为一个文件。缺点是比较大。即使您只为新版本更改几行代码,整个文件也会不同。

制作一个引用其他 JAR 文件的“瘦”JAR

在这种方法中,JAR 仅包含您的应用程序 类。它的清单文件引用类路径,但您还需要为依赖项提供 JAR 文件。要收集这些,请使用 Maven Dependency Plugin, more specifically the copy-dependencies goal。您可以这样配置它:

<executions>
  <execution>
    <id>copy</id>
    <phase>package</phase>
    <goals>
      <goal>copy-dependencies</goal>
    </goals>
    <configuration>
      <outputDirectory>${project.build.directory}/libs</outputDirectory>
      <stripVersion>true</stripVersion>
    </configuration>
  </execution>
</executions>

现在您在 target/lib 中拥有所有依赖项 JAR 文件,最后一件事是确保“瘦”JAR 引用这些文件。为此,配置 Maven Jar Plugin:

<configuration>
  <archive>
    <manifest>
      <addClasspath>true</addClasspath>
      <classpathPrefix>lib/</classpathPrefix>
      <mainClass>com.mycompany.MyApplication</mainClass>
    </manifest>
  </archive>
</configuration>

在这种方法中,如果您只更改几行应用程序代码,则只会替换应用程序 JAR - 依赖项 JAR 保持不变。不利的一面是,它要求您分发的不是一个文件,而是一个目录结构:应用程序 JAR 文件以及 lib/ 文件夹及其内容。