Gradle 运行时出现 NoClassDefFoundError
NoClassDefFoundError at Runtime with Gradle
我正在使用 gradle 作为 JavaFX 插件。
即使在 运行nig distribution/ 处的可执行文件之后,一切都完美运行,除了一个 class:CloseableHttpClient
出于多种目的,我创建了如下对象:
CloseableHttpClient client = HttpClients.createDefault();
运行IDE中的程序没有问题,一切正常。但是,如果我构建并尝试 运行 .exe 文件,我会得到以下 Throwable
-StackTrace:
java.lang.NoClassDefFoundError: Could not initialize class org.apache.http.conn.ssl.SSLConnectionSocketFactory
at org.apache.http.impl.client.HttpClientBuilder.build(HttpClientBuilder.java:955)
at org.apache.http.impl.client.HttpClients.createDefault(HttpClients.java:58)
at ch.itcb.tools.lom.util.JsonSimpleUtil.http(JsonSimpleUtil.java:29)...
我真的不明白。怎么可能只有这个 class 找不到,而我所有其他 class 都找到了?
我的 build.gradle 文件:
apply plugin: 'java'
apply plugin: 'eclipse'
apply from: 'javafx.plugin'
sourceCompatibility = 1.8
version = '0.1'
jar {
manifest {
attributes 'Implementation-Title': 'LogoffManager',
'Implementation-Version': version
}
}
repositories {
mavenCentral()
}
dependencies {
compile fileTree(dir: 'lib', include: ['*.jar'])
compile 'ch.qos.logback:logback-classic:1.1.3'
compile 'org.apache.httpcomponents:httpclient:4.5.1'
compile 'com.googlecode.json-simple:json-simple:1.1'
compile group: 'commons-collections', name: 'commons-collections', version: '3.2'
testCompile group: 'junit', name: 'junit', version: '4.+'
}
test {
systemProperties 'property': 'value'
}
uploadArchives {
repositories {
flatDir {
dirs 'repos'
}
}
}
如果您需要更多信息,请发表评论。谢谢
这是一个很好的问题,我刚才在研究 Java 开发人员可以得到 class 路径乐趣的许多方法的示例时遇到了这个问题:-)
我从您的 build.gradle 的最小版本开始(仅包括直接相关的内容),具体而言:
plugins {
id 'java'
}
sourceCompatibility = 1.8
repositories {
mavenCentral()
}
jar {
manifest {
attributes 'Main-Class': 'com.oliverlockwood.Main'
}
}
dependencies {
compile 'org.apache.httpcomponents:httpclient:4.5.1'
}
我的 'Main' class,在此上下文中,使用您的代码示例,即:
package com.oliverlockwood;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
public class Main {
public static void main(String[] args) {
CloseableHttpClient client = HttpClients.createDefault();
}
}
在这个阶段,我可以 运行 gradle clean build
然后是 java -jar build/libs/33106520.jar
(我的项目是根据这个 Whosebug 问题命名的)我看到了这个:
Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/http/impl/client/HttpClients
at com.oliverlockwood.Main.main(Main.java:8)
Caused by: java.lang.ClassNotFoundException: org.apache.http.impl.client.HttpClients
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
这与您的错误略有不同,但在我们挖掘和重现之前,让我强调一下:这个错误和您看到的错误都是在 运行 时间 classloader 无法找到它需要的 class。有一篇非常好的博客 post here,其中详细介绍了编译时 classpath 和 运行time classpaths 之间的区别。
如果我 运行 gradle dependencies
我可以看到我的项目的 运行 时间依赖性:
runtime - Runtime classpath for source set 'main'.
\--- org.apache.httpcomponents:httpclient:4.5.1
+--- org.apache.httpcomponents:httpcore:4.4.3
+--- commons-logging:commons-logging:1.2
\--- commons-codec:commons-codec:1.9
我将这些手动添加到我的 运行time classpath 中。 (为了记录,这通常不被认为是好的做法;但为了实验,我将这些罐子复制到我的 build/libs
文件夹和 运行 和 java -cp build/libs/33106520.jar:build/libs/* com.oliverlockwood.Main
。有趣的是,这无法重现您的确切问题。回顾一下:
- 在 运行 时没有
org.apache.httpcomponents:httpclient
可用,然后我们失败,因为找不到 HttpClients
jar。
- 如果
org.apache.httpcomponents:httpclient:4.5.1
在 运行 时可用,那么您的问题就不会出现 - 我注意到 class 您的构建无法找到 (org.apache.http.conn.ssl.SSLConnectionSocketFactory
) 同一个 Apache 库的一部分,这确实非常可疑。
我怀疑您的 运行time classpath 包含 Apache httpclient 库的不同版本。由于那里有很多版本,我不会测试每一个组合,所以我会给你以下建议。
- 如果您想完全了解问题的根本原因,请确切地确定您的错误案例中存在哪些 jar(包括它们的版本)运行时间 class 路径,如果您正在创建一个胖罐子,则包括包装在您内部的任何罐子(第 3 点中有更多相关信息)。如果您在这里分享这些细节,那就太好了;根本原因分析通常可以帮助大家更好地理解:-)
- 尽可能避免以
compile fileTree(dir: 'lib', include: ['*.jar'])
的方式使用依赖项。基于 Maven 或 JCenter 等存储库的托管依赖项比 运行dom 目录中的依赖项更容易一致地使用。如果这些是您不想发布到开源工件存储库的内部库,那么可能值得设置一个本地 Nexus 实例或类似实例。
- 考虑生成 "fat jar" 而不是 "thin jar" - 这意味着所有 运行 时间依赖项都打包在您构建的 jar 中。我推荐一个很好的 Shadow plugin for Gradle - 在我的
build.gradle
和 运行ning gradle clean shadow
中使用这个,我能够 运行 java -jar
就好了,不需要手动添加任何东西到我的 classpath.
对于Spring boot用户,一行代码即可解决。我正在使用 Gradle/Kotlin
,所以:
id("org.springframework.boot") version "2.5.5"
在您的 build.gradle.kts
的 plugins {}
部分内
有关详细信息,请访问 Spring Boot Gradle Plugin Reference Guide。
就我而言,我在 3 个月后打开了我的 IntelliJ,出现了一些运行时错误,例如 noclassdefounderror。我必须***刷新gradle ***,然后错误就消失了。
我正在使用 gradle 作为 JavaFX 插件。
即使在 运行nig distribution/ 处的可执行文件之后,一切都完美运行,除了一个 class:CloseableHttpClient
出于多种目的,我创建了如下对象:
CloseableHttpClient client = HttpClients.createDefault();
运行IDE中的程序没有问题,一切正常。但是,如果我构建并尝试 运行 .exe 文件,我会得到以下 Throwable
-StackTrace:
java.lang.NoClassDefFoundError: Could not initialize class org.apache.http.conn.ssl.SSLConnectionSocketFactory
at org.apache.http.impl.client.HttpClientBuilder.build(HttpClientBuilder.java:955)
at org.apache.http.impl.client.HttpClients.createDefault(HttpClients.java:58)
at ch.itcb.tools.lom.util.JsonSimpleUtil.http(JsonSimpleUtil.java:29)...
我真的不明白。怎么可能只有这个 class 找不到,而我所有其他 class 都找到了?
我的 build.gradle 文件:
apply plugin: 'java'
apply plugin: 'eclipse'
apply from: 'javafx.plugin'
sourceCompatibility = 1.8
version = '0.1'
jar {
manifest {
attributes 'Implementation-Title': 'LogoffManager',
'Implementation-Version': version
}
}
repositories {
mavenCentral()
}
dependencies {
compile fileTree(dir: 'lib', include: ['*.jar'])
compile 'ch.qos.logback:logback-classic:1.1.3'
compile 'org.apache.httpcomponents:httpclient:4.5.1'
compile 'com.googlecode.json-simple:json-simple:1.1'
compile group: 'commons-collections', name: 'commons-collections', version: '3.2'
testCompile group: 'junit', name: 'junit', version: '4.+'
}
test {
systemProperties 'property': 'value'
}
uploadArchives {
repositories {
flatDir {
dirs 'repos'
}
}
}
如果您需要更多信息,请发表评论。谢谢
这是一个很好的问题,我刚才在研究 Java 开发人员可以得到 class 路径乐趣的许多方法的示例时遇到了这个问题:-)
我从您的 build.gradle 的最小版本开始(仅包括直接相关的内容),具体而言:
plugins {
id 'java'
}
sourceCompatibility = 1.8
repositories {
mavenCentral()
}
jar {
manifest {
attributes 'Main-Class': 'com.oliverlockwood.Main'
}
}
dependencies {
compile 'org.apache.httpcomponents:httpclient:4.5.1'
}
我的 'Main' class,在此上下文中,使用您的代码示例,即:
package com.oliverlockwood;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
public class Main {
public static void main(String[] args) {
CloseableHttpClient client = HttpClients.createDefault();
}
}
在这个阶段,我可以 运行 gradle clean build
然后是 java -jar build/libs/33106520.jar
(我的项目是根据这个 Whosebug 问题命名的)我看到了这个:
Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/http/impl/client/HttpClients
at com.oliverlockwood.Main.main(Main.java:8)
Caused by: java.lang.ClassNotFoundException: org.apache.http.impl.client.HttpClients
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
这与您的错误略有不同,但在我们挖掘和重现之前,让我强调一下:这个错误和您看到的错误都是在 运行 时间 classloader 无法找到它需要的 class。有一篇非常好的博客 post here,其中详细介绍了编译时 classpath 和 运行time classpaths 之间的区别。
如果我 运行 gradle dependencies
我可以看到我的项目的 运行 时间依赖性:
runtime - Runtime classpath for source set 'main'.
\--- org.apache.httpcomponents:httpclient:4.5.1
+--- org.apache.httpcomponents:httpcore:4.4.3
+--- commons-logging:commons-logging:1.2
\--- commons-codec:commons-codec:1.9
我将这些手动添加到我的 运行time classpath 中。 (为了记录,这通常不被认为是好的做法;但为了实验,我将这些罐子复制到我的 build/libs
文件夹和 运行 和 java -cp build/libs/33106520.jar:build/libs/* com.oliverlockwood.Main
。有趣的是,这无法重现您的确切问题。回顾一下:
- 在 运行 时没有
org.apache.httpcomponents:httpclient
可用,然后我们失败,因为找不到HttpClients
jar。 - 如果
org.apache.httpcomponents:httpclient:4.5.1
在 运行 时可用,那么您的问题就不会出现 - 我注意到 class 您的构建无法找到 (org.apache.http.conn.ssl.SSLConnectionSocketFactory
) 同一个 Apache 库的一部分,这确实非常可疑。
我怀疑您的 运行time classpath 包含 Apache httpclient 库的不同版本。由于那里有很多版本,我不会测试每一个组合,所以我会给你以下建议。
- 如果您想完全了解问题的根本原因,请确切地确定您的错误案例中存在哪些 jar(包括它们的版本)运行时间 class 路径,如果您正在创建一个胖罐子,则包括包装在您内部的任何罐子(第 3 点中有更多相关信息)。如果您在这里分享这些细节,那就太好了;根本原因分析通常可以帮助大家更好地理解:-)
- 尽可能避免以
compile fileTree(dir: 'lib', include: ['*.jar'])
的方式使用依赖项。基于 Maven 或 JCenter 等存储库的托管依赖项比 运行dom 目录中的依赖项更容易一致地使用。如果这些是您不想发布到开源工件存储库的内部库,那么可能值得设置一个本地 Nexus 实例或类似实例。 - 考虑生成 "fat jar" 而不是 "thin jar" - 这意味着所有 运行 时间依赖项都打包在您构建的 jar 中。我推荐一个很好的 Shadow plugin for Gradle - 在我的
build.gradle
和 运行ninggradle clean shadow
中使用这个,我能够 运行java -jar
就好了,不需要手动添加任何东西到我的 classpath.
对于Spring boot用户,一行代码即可解决。我正在使用 Gradle/Kotlin
,所以:
id("org.springframework.boot") version "2.5.5"
在您的 build.gradle.kts
的plugins {}
部分内
有关详细信息,请访问 Spring Boot Gradle Plugin Reference Guide。
就我而言,我在 3 个月后打开了我的 IntelliJ,出现了一些运行时错误,例如 noclassdefounderror。我必须***刷新gradle ***,然后错误就消失了。