NoClassDefFoundError at Runtime with Gradle

我使用gradle作为JavaFX插件。即使在构建和运行可执行的分发/之后,一切都完美地工作,除了一个类:CloseableHttpClient

出于几个目的,我创建了以下对象,如下所示:

CloseableHttpClient client = HttpClients.createDefault();

在IDE中运行程序没有问题,一切正常。但是,如果我构建并尝试运行.exe文件,我会得到以下 -StackTrace:Throwable

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'
       }
    }
}

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


答案 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'
}

在此上下文中,我的'Main'类使用您的代码示例,即:

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();
    }
}

在这个阶段,我可以先运行(我的项目是以这个StackOverflow问题命名的),我看到这个:gradle clean buildjava -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)

这与你的错误有细微的不同,但在我们挖掘和重现它之前,让我强调一点:这个错误和你看到的错误都是在运行时当类装入器找不到它需要的类时引起的。这里有一篇很好的博客文章,详细介绍了编译时类路径和运行时类路径之间的区别。

如果我运行,我可以看到我的项目的运行时依赖项: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/libsjava -cp build/libs/33106520.jar:build/libs/* com.oliverlockwood.Main

  • 如果在运行时不可用,则我们将失败,因为找不到该 jar。org.apache.httpcomponents:httpclientHttpClients
  • 在运行时可用,那么你的问题就不会显现出来 - 我注意到你的构建无法找到的类()是同一个Apache库的一部分,这确实非常可疑。org.apache.httpcomponents:httpclient:4.5.1org.apache.http.conn.ssl.SSLConnectionSocketFactory

我怀疑你的运行时类路径包含不同版本的Apache httpclient库。由于有很多lotta版本,我不打算测试每一个组合,所以我会给你留下以下建议。

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

答案 2

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

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

在你的 build.gradle.kts 部分內部plugins {}

有关更多信息,请访问 Spring Boot Gradle 插件参考指南


推荐