之前我是通过Eclipse的“导出”功能创建可运行的JAR文件,但现在我已经切换到了IntelliJ IDEA和Gradle用于构建自动化。
这里有一些文章建议使用“应用程序”插件,但这并没有完全达到我期望的结果(只是一个JAR文件,没有启动脚本或类似的东西)。
如何才能达到与Eclipse的“导出”对话框相同的结果?
之前我是通过Eclipse的“导出”功能创建可运行的JAR文件,但现在我已经切换到了IntelliJ IDEA和Gradle用于构建自动化。
这里有一些文章建议使用“应用程序”插件,但这并没有完全达到我期望的结果(只是一个JAR文件,没有启动脚本或类似的东西)。
如何才能达到与Eclipse的“导出”对话框相同的结果?
可执行的jar文件只是一个包含其清单中的Main-Class条目的jar文件。因此,您只需要配置jar任务以添加此条目到其清单中:
jar {
manifest {
attributes 'Main-Class': 'com.foo.bar.MainClass'
}
}
application {
// Define the main class for the application.
mainClass = 'com.foo.bar.MainClass'
}
jar {
manifest {
attributes 'Main-Class': application.mainClass
}
}
JB Nizet和Jorge_B的回答都是正确的。
简单来说,使用Gradle创建可执行的JAR只需要向manifest中添加适当的条目即可。然而,在实践中,由于常常需要包含在类路径中的依赖项,因此这种方法会变得比较棘手。
application plugin提供了一种替代方法;它不是创建一个可执行的JAR,而是提供:
run
任务,以便轻松地直接从构建中运行应用程序installDist
任务生成了一个目录结构,包括已构建的JAR以及所有它所依赖的JAR和启动脚本,将它们组合成一个可运行的程序distZip
和distTar
任务创建包含完整应用程序分发(启动脚本和JAR文件)的存档文件第三种方法是创建所谓的“fat JAR”,它是一个可执行的JAR,不仅包含您组件的代码,还包括其所有依赖项。有几个不同的插件使用这种方法。我已经包含了我知道的一些链接;我相信还有更多。
.tar
/.zip
文件? - Tobiq对我来说最简单的解决方案是使用gradle-shadow-plugin
除了应用插件外,需要做的只有:
配置jar任务以将您的主类放入清单中。
jar {
manifest {
attributes 'Main-Class': 'com.my.app.Main'
}
}
运行 Gradle 任务。./gradlew shadowJar
从build/libs/获取app-version-all.jar,最后通过以下方式执行:
java -jar app-version-all.jar
build.gradle
示例。链接:https://gist.github.com/TurekBot/4a3cd406cb52dfd8bfb8022b982f0f6c。 - Brad Turek'com.my.app.MainKt'
。没有更多信息,我无法为您提供更多帮助。 - byxor正如其他人所指出的,为了使一个jar文件可以执行,应用程序的入口点必须在清单文件中的Main-Class
属性中设置。如果依赖的类文件不在同一目录下,则需要在清单文件的Class-Path
条目中进行设置。
我尝试过各种插件组合和其他方法,以简单的任务创建可执行的jar文件,并以某种方式包含依赖项。所有插件似乎都有缺陷,但最终我得到了我想要的结果。没有神秘的脚本,没有污染构建目录的数百个不同的小文件,一个相当干净的构建脚本文件,最重要的是:不会将数百万个外来第三方类文件合并到我的jar存档中。
以下内容是从这里复制粘贴而来,供您参考。
[如何]创建一个分发zip文件,在子目录/lib
中带有依赖的jar并将所有依赖项添加到清单文件的Class-Path
条目中:
apply plugin: 'java'
apply plugin: 'java-library-distribution'
repositories {
mavenCentral()
}
dependencies {
compile 'org.apache.commons:commons-lang3:3.3.2'
}
// Task "distZip" added by plugin "java-library-distribution":
distZip.shouldRunAfter(build)
jar {
// Keep jar clean:
exclude 'META-INF/*.SF', 'META-INF/*.DSA', 'META-INF/*.RSA', 'META-INF/*.MF'
manifest {
attributes 'Main-Class': 'com.somepackage.MainClass',
'Class-Path': configurations.runtime.files.collect { "lib/$it.name" }.join(' ')
}
// How-to add class path:
// https://dev59.com/m2Eh5IYBdhLWcg3wNxPM
// https://gist.github.com/simon04/6865179
}
可以在 这里 找到托管的结果。
结果可以在 build/distributions
中找到,并且解压后的内容如下:
lib/commons-lang3-3.3.2.jar
MyJarFile.jar
MyJarFile.jar#META-INF/MANIFEST.mf
的内容如下:
Manifest-Version: 1.0
Main-Class: com.somepackage.MainClass
Class-Path: lib/commons-lang3-3.3.2.jar
lib
目录中,而是在 lib
的父目录中,就像这个答案所建议的那样。这个解决方案对我来说似乎完美地解决了问题。 - thejonwithnohconfigurations.runtime.files.collect
被替换为configurations.runtimeClasspath.files.collect
。 - comfreaktasks.jar {
manifest.attributes["Main-Class"] = "com.example.MyMainClass"
// OR another notation
// manifest {
// attributes["Main-Class"] = "com.example.MyMainClass"
// }
}
tasks.jar {
manifest.attributes["Main-Class"] = "com.example.MyMainClass"
manifest.attributes["Class-Path"] = configurations
.runtimeClasspath
.get()
.joinToString(separator = " ") { file ->
"libs/${file.name}"
}
}
Class-Path
属性中使用相对URL。因此,我们不能使用Gradle依赖项的绝对路径(这也容易被更改,并且在其他系统上不可用)。如果您想使用绝对路径,也许这个解决方法会起作用。./gradlew jar
tasks.jar {
manifest.attributes["Main-Class"] = "com.example.MyMainClass"
val dependencies = configurations
.runtimeClasspath
.get()
.map(::zipTree) // OR .map { zipTree(it) }
from(dependencies)
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
}
plugins {
id("com.github.johnrengelman.shadow") version "6.0.0"
}
// Shadow task depends on Jar task, so these will be reflected for Shadow as well
tasks.jar {
manifest.attributes["Main-Class"] = "org.example.MainKt"
}
./gradlew shadowJar
java -jar my-artifact.jar
以上解决方案已经进行了以下测试:
请参阅官方Gradle文档以创建uber(fat)JAR文件。
有关清单文件的更多信息,请参阅Oracle Java文档:使用清单文件。
请注意,您的资源文件将自动包含在JAR文件中(假设它们放置在 /src/main/resources/ 目录或在构建文件中设置为资源根目录的任何自定义目录中)。要在应用程序中访问资源文件,请使用以下代码(注意名称开头的/
):
val vegetables = MyClass::class.java.getResource("/vegetables.txt").readText()
// 其他方式:
// val vegetables = object{}.javaClass.getResource("/vegetables.txt").readText()
// val vegetables = MyClass::class.java.getResourceAsStream("/vegetables.txt").reader().readText()
// val vegetables = object{}.javaClass.getResourceAsStream("/vegetables.txt").reader().readText()
var stream = MyClass.class.getResource("/vegetables.txt").openStream();
// 或者 var stream = MyClass.class.getResourceAsStream("/vegetables.txt");
var reader = new BufferedReader(new InputStreamReader(stream));
var vegetables = reader.lines().collect(Collectors.joining("\n"));
plugins {
id "org.springframework.boot" version "2.2.2.RELEASE"
}
gradle assemble
然后运行它
java -jar build/libs/*.jar
注意:您的项目不需要是SpringBoot项目才能使用此插件。
谢谢Konstantin,它像魔法一样工作,并带有一些细微差别。由于某种原因,在jar清单的一部分指定主类并没有完全生效,而是需要使用mainClassName属性。以下是包含所有要使其工作的内容的build.gradle片段:
plugins {
id 'java'
id 'com.github.johnrengelman.shadow' version '1.2.2'
}
...
...
apply plugin: 'application'
apply plugin: 'com.github.johnrengelman.shadow'
...
...
mainClassName = 'com.acme.myapp.MyClassMain'
...
...
...
shadowJar {
baseName = 'myapp'
}
installApp
不会创建 META-INF/MANIFEST.MF
文件。我做错了什么吗? - advait您可以在模块设置(或项目结构)中定义jar工件。
制作jar文件只需从“构建”菜单中单击“构建工件...”。作为奖励,您可以将所有依赖项打包到一个单独的jar文件中。
在IntelliJ IDEA 14 Ultimate上测试过。
我检查了很多链接来找到解决方案,最终执行以下步骤使其正常工作。我正在使用Gradle 2.9。
请在您的build.gradle文件中进行以下更改:
Mention plugin:
apply plugin: 'eu.appsatori.fatjar'
Provide the Buildscript:
buildscript {
repositories {
jcenter()
}
dependencies {
classpath "eu.appsatori:gradle-fatjar-plugin:0.3"
}
}
Provide the Main Class:
fatJar {
classifier 'fat'
manifest {
attributes 'Main-Class': 'my.project.core.MyMainClass'
}
exclude 'META-INF/*.DSA', 'META-INF/*.RSA', 'META-INF/*.SF'
}
Create the fatjar:
./gradlew clean fatjar
Run the fatjar from /build/libs/ :
java -jar MyFatJar.jar
chmod +x
属性是无法使 JAR 文件可执行的,绝对不同于 Eclipse 命令导出
执行的操作。 - Dims