使用 Gradle 在运行时出现 NoClassDefFoundError

Posted

技术标签:

【中文标题】使用 Gradle 在运行时出现 NoClassDefFoundError【英文标题】:NoClassDefFoundError at Runtime with Gradle 【发布时间】:2016-01-11 10:21:00 【问题描述】:

我使用 gradle 作为 JavaFX 插件。 即使在 distribution/ 构建和运行可执行文件后,一切都运行良好,除了一个类: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)...

我真的不明白。怎么可能只有这个类没有找到,而我所有的其他类都找到了?

我的 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'
       
    

如果您需要更多信息,请发表评论。谢谢。

【问题讨论】:

请分享您的 build.gradle 文件。 @Opal 完成。仍然没有找到解决方案:/ 发行版中有 apache jar 吗?它是否包含缺失的类? @JensSchauder 是的。我自己都不敢相信。 您找到解决方案了吗?你会分享吗?坦克 【参考方案1】:

这是一个很好的问题,我刚刚在研究 Java 开发人员可以通过多种方式获得类路径乐趣的示例时遇到了这个问题 :-)

我从您的 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'

在这种情况下,我的“主”类使用您的代码示例,即:

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(我的项目以这个 *** 问题命名),我看到了:

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)

这与您的错误略有不同,但在我们挖掘和重现该错误之前,让我强调一点:这个错误和您看到的错误都是在运行时导致类加载器无法找到它需要的类.有一篇相当不错的博文 here 详细介绍了编译时类路径和运行时类路径之间的区别。

如果我运行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

我将这些手动一一添加到我的运行时类路径中。 (作为记录,这通常不是好的做法;但为了实验,我将这些 jar 复制到我的 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 在运行时可用,那么您的问题就不会出现 - 我注意到您的构建找不到的类 (org.apache.http.conn.ssl.SSLConnectionSocketFactory) 是 同一个 Apache 库的一部分,它确实很可疑。

我怀疑您的运行时类路径包含 Apache httpclient 库的不同版本。由于那里有很多版本,我不会测试每一个组合,所以我会为您提供以下建议。

    如果您想完全了解问题的根本原因,请准确确定错误案例运行时类路径中存在哪些 jar(包括它们的版本),包括打包在其中的任何 jar如果您要创建一个胖罐子,则属于您的(第 3 点对此进行了更多说明)。如果您在此处分享这些详细信息,那就太好了;根本原因分析通常可以帮助每个人更好地理解:-) 尽可能避免以compile fileTree(dir: 'lib', include: ['*.jar']) 的方式使用依赖项。基于 Maven 或 JCenter 等存储库的托管依赖项比随机目录中的依赖项更容易始终如一地使用。如果这些是您不想发布到开源工件存储库的内部库,那么可能值得设置一个本地 Nexus 实例或类似实例。 考虑生成“胖 jar”而不是“瘦 jar”——这意味着所有运行时依赖项都打包在您构建的 jar 中。我推荐一个很好的Shadow plugin for Gradle - 在我的build.gradle 中使用它并运行gradle clean shadow,我能够运行java -jar 就好了,而无需手动向我的类路径添加任何内容。李>

【讨论】:

我收到了java.lang.NoClassDefFoundError: org/apache/http/config/Lookup。我一直在 Eclipse 中的Maven Dependencies 下的 Apache Http JAR(httpclient、httpcore)中寻找此类的存在,但没有找到它们。意识到 maven 选择从父 POM 解析一些其他版本的 httpcore 而不是 httpcore 版本,这是 httpclient 的直接依赖项。必须给出一个明确的 httpcore 版本,该版本与版本相同httpclient的直接依赖。 在最近的 gradle 版本中,gradle dependencies 会生成一条消息:A web-based, searchable dependency report is available by adding the --scan option.gradle build --scan 会生成一个可公开浏览的报告,其中包含您可以在完成后删除的部门。 感谢您对 deps 的提示 - 我有一个导致 NoClassDef* 的版本的 cosmo zoo。 在 IJ gradle 设置中切换到使用 IJ 进行构建现在可以解决问题【参考方案2】:

对于 Spring boot 用户,这可以通过一行代码解决。我正在使用Gradle/Kotlin,所以:

id("org.springframework.boot") version "2.5.5"

在 build.gradle.kts 的 plugins 部分中

欲了解更多信息,请访问Spring Boot Gradle Plugin Reference Guide。

【讨论】:

以上是关于使用 Gradle 在运行时出现 NoClassDefFoundError的主要内容,如果未能解决你的问题,请参考以下文章

Gradle 同步失败:评估设置时出现问题

运行应用程序时出现错误“$flutterSdkpath\packages\flutter_tools\gradle\app_plugin_loader.gradle”

颤振:第一次运行我的颤振应用程序时出现 gradle 错误

[在liferay portlet中使用skype gradle依赖项时出现错误

为啥我在构建项目时出现 Flutter gradle 错误?

在 Gradle Proguard 优化时出现大量内存错误