运行时使用Gradle时的NoClassDefFoundError [英] NoClassDefFoundError at Runtime with Gradle
问题描述
我使用gradle作为JavaFX插件。
在构建和运行 distribution / 中的可执行文件之后,除了一个类之外,一切都可以完美运行: CloseableHttpClient
出于多种目的,我创建了如下对象:
CloseableHttpClient client = HttpClients.createDefault ();
在IDE中运行程序没有问题,一切正常。但是,如果我建立并尝试运行.exe文件,我会得到以下 Throwable
-StackTrace:
java.lang.NoClassDefFoundError:无法在org.apache.http.impl.client.HttpClientBuilder.build(HttpClientBuilder)中初始化类org.apache.http.conn.ssl.SSLConnectionSocketFactory
。 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
}
}
存储库{
mavenCentral()
}
依赖项{
编译fileTree(dir:'lib',include:['* .jar'])
编译'ch.qos.logback:logback-classic:1.1.3'
编译'org.apache.httpcomponents:httpclient:4.5.1'
compile'com.googlecode.json-simple:json-simple:1.1'
编译组:'commons-collections',name:'commons- collection',version:'3.2'
testCompile group:'junit',name:'junit',version:'4. +'
}
test {
systemProperties'property':'value'
}
uploadArchives {
repositories {
flatDir {
dirs'repos'
}
$ / code $如果您需要更多信息,请写评论。 Thx。解决方案
这是一个很好的问题,我刚才在研究Java开发人员可能以多种方式类的路径有趣: - )
我从一个最小版本的build.gradle开始(仅包括直接相关的东西),特别是:
插件{
id'java'
}
sourceCompatibility = 1.8
repositories {
mavenCentral()
}
jar {
manifest {
attributes'Main-Class':'com.oliverlockwood.Main'
}
}
依赖关系{
编译'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();
$ b在这个阶段,我可以运行 gradle clean build
后跟 java -jar build / libs / 33106520.jar
(我的项目是以这个StackOverflow问题命名的),我看到这个:
线程main中的异常java.lang.NoClassDefFoundError:org / apache / http / impl / client / HttpClients $ b.b在com.oliverlockwood.Main.main(Main.java:8)
导致:java.lang.ClassNotFoundException:org.apache.http.impl.client.HttpClients
在java.net .URLClassLoader.findClass(URLClassLoader.java:381)$ b $在java.lang.ClassLoader.loadClass(ClassLoader.java:424)
在sun.misc.Launcher $ AppClassLoader.loadClass(Launcher.java:331 )
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
与你的错误有微妙的不同,但在我们挖掘并重现它之前,让我强调一下:这个错误和你看到的错误都是在运行时导致的类加载器无法找到它需要的类。 这里有相当不错的博客文章,其中有更多详细信息如果我运行 gradle dependencies
,我可以看到运行时依赖关系对于我的项目:
runtime - 源代码集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库的一个不同版本的 。由于这里有很多lotta版本,我不会测试每一个组合,所以我会给你留下以下建议。
- 如果您想完全了解问题的根源,请确切地标识 哪些jar(包括它们的版本)出现在您的错误案例运行时类路径中,包括打包的所有jar如果你正在创建一个胖罐子(在这点上更多的点3)。如果你在这里分享这些细节,那会很棒;根本原因分析通常可以帮助大家更好地理解: - )
- 尽可能避免以
编译fileTree(dir:'lib',include: [ '的* .jar'])
。基于存储库(例如Maven或JCenter)的托管依赖关系比随机目录中的依赖关系更易于一致地工作。如果这些是不想发布到开源工件存储库的内部库,则可能需要设置本地Nexus实例或类似工具。
- 考虑生成一个胖jar而不是thin jar - 这意味着所有运行时依赖关系都打包在您构建的jar中。我建议使用一个很好的 Gradle插件,并在
build.gradle
,并运行 gradle clean shadow
,我可以运行 java -jar
就好了,无需手动向我的类路径添加任何内容。
I'm using gradle as the JavaFX plugin.
Everything works perfectly even after building and runnig the excecutable at distribution/, except with one class: CloseableHttpClient
For several purposes I create the following object like this:
CloseableHttpClient client = HttpClients.createDefault();
Running the program in the IDE is no problem, everything works fine. But if I build and try to run the .exe-File I get the following 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)...
I really don't understand that. How can it be that just this class doesn't get found, but all my other classes do?
My build.gradle file:
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'
}
}
}
Please write a comment if you need more information. Thx.
解决方案 it's a good question, which I came across just now while researching examples of the many ways Java developers can end up with class path fun :-)
I started with a minimal version of your build.gradle (including only what's directly relevant), specifically:
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'
}
My 'Main' class, in this context, uses your code example, i.e.:
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();
}
}
At this stage, I can run gradle clean build
followed by java -jar build/libs/33106520.jar
(my project was named after this StackOverflow question) and I see this:
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)
This is subtly different from your error, but before we dig and reproduce that, let me emphasise something: both this error and the one you're seeing are caused at runtime when the classloader is unable to find a class that it needs. There's quite a good blog post here with some more details about the difference between compile-time classpath and runtime classpaths.
If I run gradle dependencies
I can see the runtime dependencies for my project:
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
I added these manually one-by-one to my runtime classpath. (For the record, this isn't generally considered good practice; but for the sake of the experiment, I copied these jars to my build/libs
folder and ran with java -cp build/libs/33106520.jar:build/libs/* com.oliverlockwood.Main
. Interestingly enough, this wasn't able to reproduce your exact problem. To recap:
- Without
org.apache.httpcomponents:httpclient
available at runtime, then we fail because the HttpClients
jar is not found.
- With
org.apache.httpcomponents:httpclient:4.5.1
available at runtime, then your problem does not manifest - and I note that the class your build fails to find (org.apache.http.conn.ssl.SSLConnectionSocketFactory
) is part of this same Apache library, which is very suspicious indeed.
My suspicion is then that your runtime classpath contains a different version of the Apache httpclient library. Since there's a whole lotta versions out there, I'm not going to test every single combination, so I will instead leave you with the following advice.
- If you want to fully understand the root cause of your issue, then identify exactly which jars (including their versions) are present in your error-case runtime classpath, including any jars that are packaged inside yours if you're creating a fat jar (more on this in point 3). It'd be great if you shared these details here; root cause analysis usually helps everyone to understand better :-)
- Where possible, avoid using dependencies in the manner of
compile fileTree(dir: 'lib', include: ['*.jar'])
. Managed dependencies based on a repository such as Maven or JCenter are much easier to work with consistently than dependencies in a random directory. If these are internal libraries that you don't want to publish to an open-source artifact repository, then it may be worth setting up a local Nexus instance or similar.
- Consider producing a "fat jar" instead of a "thin jar" - this means that all runtime dependencies are packaged in the jar that you build. There's a good Shadow plugin for Gradle that I'd recommend - with this in place in my
build.gradle
, and running gradle clean shadow
, I was able to run java -jar
just fine without needing to manually add anything to my classpath.
这篇关于运行时使用Gradle时的NoClassDefFoundError的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!