英文:
java-maven-idea: including external library in jar
问题
我正在制作 Telegram 机器人,需要一个 .jar 文件将其部署到云端。
我正在使用 IntelliJ IDEA 中的 Maven 进行构建,但在尝试在我的设备上运行时出现以下错误:
Exception in thread "main" java.lang.NoClassDefFoundError: org/telegram/telegrambots/bots/TelegramLongPollingBot<br>
据我所了解,这是因为 Maven 没有将这个库打包到 .jar 文件中。
我应该如何解决这个问题?
英文:
I'm making telegram bot, and I need .jar to deploy it in cloud.<br>
I'm building it with maven in intellij idea, but when trying to execute on my machine it throws this:<br>
Exception in thread "main" java.lang.NoClassDefFoundError: org/telegram/telegrambots/bots/TelegramLongPollingBot<br>
As I've understood, this happens because maven doesn't packing this lib into .jar.<br>
How can I do this?
答案1
得分: 0
大致来说,您有两个选项:
- 制作一个包含所有所需类的“厚”JAR文件。
- 制作一个引用其他JAR文件的“薄”JAR文件。
对于您的情况,最合适的选项是您自己决定的。下面是如何做到:
制作一个包含所有所需类的“厚”JAR文件
要采用这种方法,您可以使用Maven Shade Plugin。在打包阶段,您将调用其shade
目标。这将会将来自依赖项和应用程序的类一起复制到一个JAR文件中。在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>
<!--
没有这个,对签名的JAR进行重新打包会失败。
http://stackoverflow.com/questions/999489/invalid-signature-file-when-attempting-to-run-a-jar
-->
<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,具体而言是copy-dependencies
目标。您可以像这样配置它:
<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>
现在,您将所有依赖的JAR文件都放在 target/lib 中,最后一步是确保“薄”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/ 文件夹。
英文:
Roughly speaking, you have two options
- Make a "fat" JAR with all required classes present
- Make a "thin" JAR that references other JAR files
What is most suitable for your situation is something only you can decide. Here's how you do it:
Make a "fat" JAR with all required classes present
To follow this approach, you use the Maven Shade Plugin. In the package phase, you would invoke its shade
goal. That would copy classes from your dependencies as well as your application classes together into one JAR-file. It could look like this in the 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.
http://stackoverflow.com/questions/999489/invalid-signature-file-when-attempting-to-run-a-jar
-->
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
</configuration>
</execution>
</executions>
The advantage of this approach is that your application gets packaged as one file. The disadvantage is that it's rather large. Even when you change just a few lines of code for a new version, the whole file will be different.
Make a "thin" JAR that references other JAR files
In this approach, the JAR only contains your application classes. Its manifest file references the classpath, but you need to also provide the JAR file for the dependencies. To collect those, use the Maven Dependency Plugin, more specifically the copy-dependencies
goal. You configure it like so:
<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>
Now you have all dependency JAR files in target/lib, and the last thing is make sure that the "thin" JAR references those. To do so, configure the Maven Jar Plugin:
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<classpathPrefix>lib/</classpathPrefix>
<mainClass>com.mycompany.MyApplication</mainClass>
</manifest>
</archive>
</configuration>
In this approach, if you change just a few lines of your application code, only the application JAR will be replaced - the dependency JARs are left intact. On the downside, it requires you to distribute not one file but a directory structure: the application JAR file as well as the lib/ folder with its contents.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论