这是一个很好的问题,我在研究Java开发人员可以通过类路径乐趣最终获得许多方法的示例时遇到了:-)
我从build.gradle的最低版本开始(仅包括直接相关的版本),特别是:
plugins { id 'java'}sourceCompatibility = 1.8repositories { 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 -jarbuild/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-prec:commons-prec:1.9
我将这些手动一一添加到我的运行时类路径中。(记录下来,这通常不被认为是一种好习惯;但是为了进行实验,我将这些罐子复制到了我的
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时打包在您的jar中的所有jar(在第3点中对此有更多的了解。如果您在此处共享这些详细信息,那就太好了;根本原因分析通常可以帮助所有人更好地理解:-)
- 尽可能避免以方式使用依赖项
compile fileTree(dir: 'lib', include: ['*.jar'])
。与诸如随机目录中的依赖关系相比,基于Maven或JCenter之类的存储库的托管依赖关系更易于一致地使用。如果您不想将这些内部库发布到开源工件存储库中,那么可能值得设置本地Nexus实例或类似实例。 - 考虑生产“胖罐子”而不是“薄罐子”-这意味着所有运行时依赖项都打包在您构建的罐子中。我建议为Gradle提供一个很好的Shadow插件 -将其放置在我的
build.gradle
,然后运行gradle clean shadow
,我可以java -jar
很好地运行,而无需手动将任何东西添加到我的类路径中。



