如何创建具有实现依赖性的 Gradle 可执行胖 JAR?
How do I create an executable fat JAR with Gradle with implementation dependencies?
我在 Gradle 4.6 中有一个简单的项目,想为它制作一个可执行的 JAR。我试过 shadow
、gradle-fatjar-plugin
、gradle-one-jar
、spring-boot-gradle-plugin
插件,但它们都没有添加我声明为 implementation
的依赖项(我没有任何 compile
个)。它适用于 compile
例如对于 gradle-one-jar
插件,但我想要 implementation
依赖项。
您可以使用以下代码。
jar {
manifest {
attributes(
'Main-Class': 'com.package.YourClass'
)
}
from {
configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
}
}
请务必将 com.package.YourClass
替换为包含 static void main( String args[] )
.
的完全限定 class 名称
这将打包运行时依赖项。如果您需要更多信息,请查看 docs。
可以使用 Gradle Kotlin DSL 以类似的方式完成相同的任务:
val jar by tasks.getting(Jar::class) {
manifest {
attributes["Main-Class"] = "com.package.YourClass"
}
from(configurations
.runtime
// .get() // uncomment this on Gradle 6+
// .files
.map { if (it.isDirectory) it else zipTree(it) })
}
基于接受的答案,我需要添加一行代码:
task fatJar(type: Jar) {
manifest {
attributes 'Main-Class': 'com.yourpackage.Main'
}
archiveClassifier = "all"
from {
configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
}
with jar
}
没有这一行,它省略了我的源文件,只添加了依赖项:
configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
对于较新的 gradle (7+),您可能会看到此错误:
Execution failed for task ':fatJar'.
> Entry [some entry here] is a duplicate but no duplicate handling strategy has been set. Please
refer to https://docs.gradle.org/7.1/dsl/org.gradle.api.tasks.Copy.html#org.gradle.api.tasks.Copy:duplicatesStrategy
for details.
如果发生这种情况,请在 fatJar
任务中添加一个 duplicatesStrategy
,例如 duplicatesStrategy "exclude"
。
同样,对于 Gradle 7+,您只需删除 configuration.compile.collect
行,因为它不再是此版本 gradle.[=17 中的有效配置=]
from { configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } }
这条线对我来说很重要。
Kotlin 1.3.72 和 JVM 插件,Gradle6.5.1
所有这些平台的语法都在快速变化
tasks {
compileKotlin {
kotlinOptions.jvmTarget = "1.8"
}
compileTestKotlin {
kotlinOptions.jvmTarget = "1.8"
}
val main = sourceSets.main.get()
//TODO
register<Jar>("buildFatJar") {
group = "app-backend"
dependsOn(build)
// shouldRunAfter(parent!!.tasks["prepCopyJsBundleToKtor"]) -> This is for incorporating KotlinJS gradle subproject resulting js file.
manifest {
attributes["Main-Class"] = "com.app.app.BackendAppKt"
}
from(configurations.compileClasspath.get().files.map { if (it.isDirectory) it else zipTree(it) })
with(jar.get() as CopySpec)
archiveBaseName.set("${project.name}-fat")
}
}
以前的答案如今有点过时,请参阅此处了解与 gradle-7.4 一起使用的内容:
How to create a fat JAR with Gradle Kotlin script?
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
}
这里我提供Kotlin DSL(build.gradle.kts)的解决方案。
请注意,前 3 个方法修改 Gradle 的现有 Jar
任务。
方法一:将库文件放在结果JAR旁边
此方法不需要 application
或任何其他插件。
tasks.jar {
manifest.attributes["Main-Class"] = "com.example.MyMainClass"
manifest.attributes["Class-Path"] = configurations
.runtimeClasspath
.get()
.joinToString(separator = " ") { file ->
"libs/${file.name}"
}
}
请注意 Java 要求我们为 Class-Path
属性使用相对 URL。所以,我们不能使用 Gradle 依赖项的绝对路径(这也很容易被更改,并且在其他系统上不可用)。如果你想使用绝对路径,也许 会起作用。
使用以下命令创建 JAR:
./gradlew jar
默认情况下,结果 JAR 将创建在 build/libs/ 目录中。
创建 JAR 后,将库 JAR 复制到放置结果 JAR 的 libs/ sub-directory 中。确保您的库 JAR 文件的文件名中不包含 space(它们的文件名应与上面任务中的 ${file.name}
变量指定的文件名相匹配)。
方法 2:将库嵌入结果 JAR(fat 或 uber JAR)
此方法也不需要任何 Gradle 插件。
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
}
创建 JAR 与前面的方法完全相同。
方法 3:使用 Shadow plugin(创建 fat 或 uber JAR)
plugins {
id("com.github.johnrengelman.shadow") version "6.0.0"
}
// Shadow task depends on Jar task, so these configs are reflected for Shadow as well
tasks.jar {
manifest.attributes["Main-Class"] = "org.example.MainKt"
}
使用此命令创建 JAR:
./gradlew shadowJar
有关配置插件的详细信息,请参阅 Shadow documentations。
方法 4:创建新任务(而不是修改 Jar
任务)
tasks.create("MyFatJar", Jar::class) {
group = "my tasks" // OR, for example, "build"
description = "Creates a self-contained fat JAR of the application that can be run."
manifest.attributes["Main-Class"] = "com.example.MyMainClass"
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
val dependencies = configurations
.runtimeClasspath
.get()
.map(::zipTree)
from(dependencies)
with(tasks.jar.get())
}
运行 创建的 JAR
java -jar my-artifact.jar
以上解决方案经过以下测试:
- Java 17
- Gradle 7.1(将 Kotlin 1.4.31 用于 .kts 构建脚本)
见官方Gradle documentation for creating uber (fat) JARs.
有关清单的详细信息,请参阅 Oracle Java Documentation: Working with Manifest files。
有关 tasks.create()
和 tasks.register()
之间的差异,请参阅 。
请注意您的 resource files will be included in the JAR file automatically(假设它们被放置在 /src/main/resources/ 目录或在构建文件中设置为资源根目录的任何自定义目录)。要访问应用程序中的资源文件,请使用此代码(注意名称开头的 /
):
- 科特林
val vegetables = MyClass::class.java.getResource("/vegetables.txt").readText()
// Alternative ways:
// 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()
- Java
var stream = MyClass.class.getResource("/vegetables.txt").openStream();
// OR var stream = MyClass.class.getResourceAsStream("/vegetables.txt");
var reader = new BufferedReader(new InputStreamReader(stream));
var vegetables = reader.lines().collect(Collectors.joining("\n"));
我在 Gradle 4.6 中有一个简单的项目,想为它制作一个可执行的 JAR。我试过 shadow
、gradle-fatjar-plugin
、gradle-one-jar
、spring-boot-gradle-plugin
插件,但它们都没有添加我声明为 implementation
的依赖项(我没有任何 compile
个)。它适用于 compile
例如对于 gradle-one-jar
插件,但我想要 implementation
依赖项。
您可以使用以下代码。
jar {
manifest {
attributes(
'Main-Class': 'com.package.YourClass'
)
}
from {
configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
}
}
请务必将 com.package.YourClass
替换为包含 static void main( String args[] )
.
这将打包运行时依赖项。如果您需要更多信息,请查看 docs。
可以使用 Gradle Kotlin DSL 以类似的方式完成相同的任务:
val jar by tasks.getting(Jar::class) {
manifest {
attributes["Main-Class"] = "com.package.YourClass"
}
from(configurations
.runtime
// .get() // uncomment this on Gradle 6+
// .files
.map { if (it.isDirectory) it else zipTree(it) })
}
基于接受的答案,我需要添加一行代码:
task fatJar(type: Jar) {
manifest {
attributes 'Main-Class': 'com.yourpackage.Main'
}
archiveClassifier = "all"
from {
configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
}
with jar
}
没有这一行,它省略了我的源文件,只添加了依赖项:
configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
对于较新的 gradle (7+),您可能会看到此错误:
Execution failed for task ':fatJar'.
> Entry [some entry here] is a duplicate but no duplicate handling strategy has been set. Please
refer to https://docs.gradle.org/7.1/dsl/org.gradle.api.tasks.Copy.html#org.gradle.api.tasks.Copy:duplicatesStrategy
for details.
如果发生这种情况,请在 fatJar
任务中添加一个 duplicatesStrategy
,例如 duplicatesStrategy "exclude"
。
同样,对于 Gradle 7+,您只需删除 configuration.compile.collect
行,因为它不再是此版本 gradle.[=17 中的有效配置=]
from { configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } }
这条线对我来说很重要。
Kotlin 1.3.72 和 JVM 插件,Gradle6.5.1
所有这些平台的语法都在快速变化
tasks {
compileKotlin {
kotlinOptions.jvmTarget = "1.8"
}
compileTestKotlin {
kotlinOptions.jvmTarget = "1.8"
}
val main = sourceSets.main.get()
//TODO
register<Jar>("buildFatJar") {
group = "app-backend"
dependsOn(build)
// shouldRunAfter(parent!!.tasks["prepCopyJsBundleToKtor"]) -> This is for incorporating KotlinJS gradle subproject resulting js file.
manifest {
attributes["Main-Class"] = "com.app.app.BackendAppKt"
}
from(configurations.compileClasspath.get().files.map { if (it.isDirectory) it else zipTree(it) })
with(jar.get() as CopySpec)
archiveBaseName.set("${project.name}-fat")
}
}
以前的答案如今有点过时,请参阅此处了解与 gradle-7.4 一起使用的内容: How to create a fat JAR with Gradle Kotlin script?
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
}
这里我提供Kotlin DSL(build.gradle.kts)的解决方案。
请注意,前 3 个方法修改 Gradle 的现有 Jar
任务。
方法一:将库文件放在结果JAR旁边
此方法不需要 application
或任何其他插件。
tasks.jar {
manifest.attributes["Main-Class"] = "com.example.MyMainClass"
manifest.attributes["Class-Path"] = configurations
.runtimeClasspath
.get()
.joinToString(separator = " ") { file ->
"libs/${file.name}"
}
}
请注意 Java 要求我们为 Class-Path
属性使用相对 URL。所以,我们不能使用 Gradle 依赖项的绝对路径(这也很容易被更改,并且在其他系统上不可用)。如果你想使用绝对路径,也许
使用以下命令创建 JAR:
./gradlew jar
默认情况下,结果 JAR 将创建在 build/libs/ 目录中。
创建 JAR 后,将库 JAR 复制到放置结果 JAR 的 libs/ sub-directory 中。确保您的库 JAR 文件的文件名中不包含 space(它们的文件名应与上面任务中的 ${file.name}
变量指定的文件名相匹配)。
方法 2:将库嵌入结果 JAR(fat 或 uber JAR)
此方法也不需要任何 Gradle 插件。
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
}
创建 JAR 与前面的方法完全相同。
方法 3:使用 Shadow plugin(创建 fat 或 uber JAR)
plugins {
id("com.github.johnrengelman.shadow") version "6.0.0"
}
// Shadow task depends on Jar task, so these configs are reflected for Shadow as well
tasks.jar {
manifest.attributes["Main-Class"] = "org.example.MainKt"
}
使用此命令创建 JAR:
./gradlew shadowJar
有关配置插件的详细信息,请参阅 Shadow documentations。
方法 4:创建新任务(而不是修改 Jar
任务)
tasks.create("MyFatJar", Jar::class) {
group = "my tasks" // OR, for example, "build"
description = "Creates a self-contained fat JAR of the application that can be run."
manifest.attributes["Main-Class"] = "com.example.MyMainClass"
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
val dependencies = configurations
.runtimeClasspath
.get()
.map(::zipTree)
from(dependencies)
with(tasks.jar.get())
}
运行 创建的 JAR
java -jar my-artifact.jar
以上解决方案经过以下测试:
- Java 17
- Gradle 7.1(将 Kotlin 1.4.31 用于 .kts 构建脚本)
见官方Gradle documentation for creating uber (fat) JARs.
有关清单的详细信息,请参阅 Oracle Java Documentation: Working with Manifest files。
有关 tasks.create()
和 tasks.register()
之间的差异,请参阅
请注意您的 resource files will be included in the JAR file automatically(假设它们被放置在 /src/main/resources/ 目录或在构建文件中设置为资源根目录的任何自定义目录)。要访问应用程序中的资源文件,请使用此代码(注意名称开头的 /
):
- 科特林
val vegetables = MyClass::class.java.getResource("/vegetables.txt").readText() // Alternative ways: // 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()
- Java
var stream = MyClass.class.getResource("/vegetables.txt").openStream(); // OR var stream = MyClass.class.getResourceAsStream("/vegetables.txt"); var reader = new BufferedReader(new InputStreamReader(stream)); var vegetables = reader.lines().collect(Collectors.joining("\n"));