kotlin - Jar made by gradle's jar task is not "fat" enough
问题描述
...obviously some necessary things are not included from the dependencies.
Once it reaches a call to an external library, it breaks, either with ClassNotFoundException, or without a word.
I started with this skeleton project.
Relevant changes in build.gradle
:
application {
mainClassName = 'net.laca.FoKt'
}
(my main function is in fo.kt)
dependencies {
//...
compile "com.sparkjava:spark-core:2.9.3"
implementation 'com.google.code.gson:gson:2.8.6'
implementation fileTree('libs') { include '*.jar' }
}
jar {
archiveBaseName = 'csira'
// Uncommend the last two lines to build a "fat" jar with `./gradlew jar`,
// and run it without Gradle's help: `java -jar build/libs/skeleton.jar`
manifest { attributes 'Main-Class': 'net.laca.FoKt' }
from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
}
versions: Kotlin 1.4.20, Java 11, Gradle 6.7.1
Allegedly it should work this way. As it does if I start it with gradle run
.
But when I start it with java -jar build/libs/csira.jar
after gradle jar
, it doesn't.
Relevant parts of fo.kt
:
package net.laca
import spark.Spark.*
import com.google.gson.GsonBuilder
fun main(args: Array<String>) {
before("/*")
{ req, res ->
res.type("application/json")
println("hívás: ${req.requestMethod()} ${req.pathInfo()} " + req.queryString())
println(GsonBuilder().create().toJson(req.queryMap().toMap())) //line 14
//...
}
At GsonBuilder it breaks:
java.lang.NoClassDefFoundError: com/google/gson/GsonBuilder
at net.laca.FoKt$main$1.handle(fo.kt:14)
at spark.FilterImpl$1.handle(FilterImpl.java:73)
at spark.http.matching.BeforeFilters.execute(BeforeFilters.java:48)
at spark.http.matching.MatcherFilter.doFilter(MatcherFilter.java:133)
at ...
...
Caused by: java.lang.ClassNotFoundException: com.google.gson.GsonBuilder
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:602)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:522)
... 19 more
And when I take/comment out the 14th line, and it reaches a call to my own jar in /libs
:
get("/whatever")
{
println("before")
com.zz.app.MyScalaClass.apply().myFun()
println("after")
}
then the last thing I see is before
, the rest is silence.
解决方案
发生这种情况是因为您的jar
任务配置不正确。要了解原因,请查看您的依赖项:
dependencies {
//...
compile "com.sparkjava:spark-core:2.9.3"
implementation 'com.google.code.gson:gson:2.8.6'
implementation fileTree('libs') { include '*.jar' }
}
您同时使用compile
和implementation
配置。前者已被弃用,不应顺便使用。
然后看jar
任务:
jar {
archiveBaseName = 'csira'
// Uncommend the last two lines to build a "fat" jar with `./gradlew jar`,
// and run it without Gradle's help: `java -jar build/libs/skeleton.jar`
manifest { attributes 'Main-Class': 'net.laca.FoKt' }
from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
}
该from
部分指示 Gradle 仅从配置中收集所有依赖项compile
,这将完全忽略implementation
配置。
虽然您可以在任何地方将“编译”更改为“实现”,但构建胖 jar 的正确方法是从runtimeClasspath
配置中实际收集。这个扩展了其他配置,例如compile
and implementation
,但runtimeOnly
您将来可能会发现它很方便。
Gradle 用户指南中实际上也有一个如何执行此操作的示例。为了适应您的项目,它应该如下所示:
jar {
archiveBaseName = 'csira'
manifest { attributes 'Main-Class': 'net.laca.FoKt' }
dependsOn configurations.runtimeClasspath
from {
configurations.runtimeClasspath.findAll { it.name.endsWith('jar') }.collect { zipTree(it) }
}
}
额外的dependsOn
行确保runtimeClasspath
在尝试使用之前完全解析配置。另一个区别是它只收集 jar 文件。
推荐阅读
- sbt - 用于创建 fat jar 的单个 sbt 文件
- reporting-services - 在 SSRS 报表生成器中添加一个参数,其中包含字符串/varchar 值,但 SQL 中的字段是数字
- mysql - Postgres 删除相关数据表的过程
- reactjs - 如何在反应中从嵌套数组中获取计数
- javascript - 令人困惑的 webpack 语法。尝试从 2017 年的旧教程中修复 webpack.config 文件
- mysql - Ansible 无法访问数据库 Mysql
- node.js - Express-validator 不想在数据库中找到任何重复项
- javascript - gtag链接器不适用于JS form.submit()
- java - 如何发送 JSON 请求并从服务器获取 JSON 响应
- r - R Shiny Datatable 子行选择和信息问题