如何使用 Gradle 创建具有实现依赖项的可执行胖 jar
Posted
技术标签:
【中文标题】如何使用 Gradle 创建具有实现依赖项的可执行胖 jar【英文标题】:How do I create an executable fat JAR with Gradle with implementation dependencies? 【发布时间】:2018-08-23 00:02:09 【问题描述】:我在 Gradle 4.6 中有一个简单的项目,并且想制作一个可执行的 jar。我试过shadow
、gradle-fatjar-plugin
、gradle-one-jar
、spring-boot-gradle-plugin
插件,但它们都没有添加我声明为implementation
的依赖项(我没有任何compile
的)。它适用于compile
,例如对于gradle-one-jar
插件,但我想拥有implementation
依赖项。
【问题讨论】:
【参考方案1】:根据接受的答案,我需要再添加一行代码:
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.
如果发生这种情况,请将 duplicatesStrategy
添加到 fatJar
任务中,例如 duplicatesStrategy "exclude"
。
同样,对于 Gradle 7+,您只需删除 configuration.compile.collect
行,因为它在此版本的 gradle 中不再是有效配置。
【讨论】:
这在 Gradle 6.0.1 中所有其他人都没有的地方有效。奇怪的是,Gradle 4.x 中似乎不需要第二行,但是一旦升级到 Gradle 6 或 5,我的旧构建配置突然停止创建胖 JAR。 Gradle 6.5 报告以下警告:编译配置已被弃用以解决问题。这将在 Gradle 7.0 中失败并出现错误。请改为解析 compileClasspath 配置。用“compileClasspath”替换“compile”修复了它。 工作得很好,不知道你是怎么找到这个的,但你在做上帝的工作!! @jbel 谢谢!我通过结合另一个帖子的信息偶然发现它 对我来说有点奇怪,尝试运行一个 github 项目(已使用 gradle 包装器设置)并不断收到依赖项的“classdefnotfoundexception”错误。我检查了子模块中的 gradle.build 文件,发现依赖项是使用“实现”关键字(而不是“编译”)导入的。一旦我为 jar 任务添加了“configuration.runtimeClasspath”行,问题就解决了。【参考方案2】:同样的任务可以使用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) )
【讨论】:
【参考方案3】:Kotlin 1.3.72 & JVM 插件,Gradle 6.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")
【讨论】:
【参考方案4】:from configurations.runtimeClasspath.collect it.isDirectory() ? it : zipTree(it)
这条线对我来说很重要。
【讨论】:
【参考方案5】:您可以使用以下代码。
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。
【讨论】:
fat jar 的名称是什么,我在哪个目录中找到它? @VijayKumar 矿位于build/libs
之下。
您可能想使用configurations.compileClasspath.filter it.exists() .collect it.isDirectory() ? it : zipTree(it)
。这样,如果您有不存在的类路径条目,它就不会挂断。我正在使用 Kotlin,所以 java 源类路径条目没有任何内容,而且它变得非常暴躁。
当我使用这段代码时,所有的库类都包含在uberjar中,但源目录中的类没有。
伙计,你为我节省了很多时间。网上有一个howto,但是有问题:)以上是关于如何使用 Gradle 创建具有实现依赖项的可执行胖 jar的主要内容,如果未能解决你的问题,请参考以下文章
在 Gradle 中,如何生成具有解析为实际使用版本的动态依赖项的 POM 文件?