运行时使用Gradle的NoClassDefFoundError

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

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

 CloseableHttpClient client = HttpClients.createDefault(); 

在IDE中运行程序没问题,一切正常。 但是如果我构建并尝试运行.exe-File,我会得到以下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开发人员最终可以获得类路径乐趣的多种方式的例子:-)

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

在这个阶段,我可以运行gradle clean build然后运行java -jar build/libs/33106520.jar (我的项目以此StackOverflow问题命名),我看到了:

 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/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库。 由于那里有很多版本,我不打算测试每一个组合,所以我会给你留下以下建议。

  1. 如果您想完全理解问题的根本原因,那么确定您的错误案例运行时类路径中确切存在哪些jar(包括它们的版本),包括如果您正在创建一个胖jar,则包含在您的内部包装的任何jar(更多关于这点3)。 如果你在这里分享这些细节,那就太好了; 根本原因分析通常可以帮助每个人更好地理解:-)
  2. 尽可能避免以compile fileTree(dir: 'lib', include: ['*.jar'])的方式使用依赖项。 基于诸如Maven或JCenter之类的存储库的托管依赖项比随机目录中的依赖项更容易使用。 如果这些是您不想发布到开源工件存储库的内部库,则可能需要设置本地Nexus实例或类似实例。
  3. 考虑生成一个“胖jar”而不是“瘦jar” – 这意味着所有运行时依赖项都打包在您构建的jar中。 我建议有一个很好的Gradle Shadow插件 – 在我的build.gradle中实现这build.gradle ,并运行gradle clean shadow ,我能够运行java -jar ,而无需手动向我的类路径添加任何内容。