【问题标题】:NoClassDefFoundError while trying to run my jar with java.exe -jar...what's wrong?尝试使用 java.exe -jar 运行我的 jar 时出现 NoClassDefFoundError ...怎么了?
【发布时间】:2023-03-08 19:36:01
【问题描述】:

我有一个应用程序,我正试图将其包装到一个 jar 中以便于部署。当作为可从 CLASSPATH 访问的一组类运行时,应用程序编译并运行良好(在 Windows cmd 窗口中)。但是当我打开我的类并尝试在同一个 cmd 窗口中使用 java 1.6 运行它时,我开始收到异常:

C:\dev\myapp\src\common\datagen>C:/apps/jdk1.6.0_07/bin/java.exe -classpath C:\myapp\libs\commons -logging-1.1.jar -server -jar DataGen.jar
Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/commons/logging/LogFactory
    at com.example.myapp.fomc.common.datagen.DataGenerationTest.<clinit>(Unknown Source)
Caused by: java.lang.ClassNotFoundException: org.apache.commons.logging.LogFactory
    at java.net.URLClassLoader$1.run(URLClassLoader.java:200)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:276)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:251)
    at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:319)
    ... 1 more

有趣的是,有问题的 LogFactory 似乎在 commons-logging-1.1.jar 中,它位于指定的类路径中。 jar 文件(是的,确实存在):

C:\dev\myapp\src\common\datagen>dir C:\myapp\libs\commons-logging-1.1.jar
 Volume in drive C is Local Disk
 Volume Serial Number is ECCD-A6A7

 Directory of C:\myapp\libs

12/11/2007  11:46 AM            52,915 commons-logging-1.1.jar
           1 File(s)         52,915 bytes
           0 Dir(s)  10,956,947,456 bytes free

commons-logging-1.1.jar 文件的内容:

C:\dev\myapp\src\common\datagen>jar -tf C:\myapp\libs\commons-logging-1.1.jar
META-INF/
META-INF/MANIFEST.MF
org/
org/apache/
org/apache/commons/
org/apache/commons/logging/
org/apache/commons/logging/impl/
META-INF/LICENSE.txt
META-INF/NOTICE.txt
org/apache/commons/logging/Log.class
org/apache/commons/logging/LogConfigurationException.class
org/apache/commons/logging/LogFactory$1.class
org/apache/commons/logging/LogFactory$2.class
org/apache/commons/logging/LogFactory$3.class
org/apache/commons/logging/LogFactory$4.class
org/apache/commons/logging/LogFactory$5.class
org/apache/commons/logging/LogFactory.class
... (more classes in commons-logging-1.1 ...)

是的,commons-logging 有 LogFactory 类。最后,我的 jar 清单的内容:

Manifest-Version: 1.0
Ant-Version: Apache Ant 1.6.5
Created-By: 10.0-b23 (Sun Microsystems Inc.)
Main-Class: com.example.myapp.fomc.common.datagen.DataGenerationTest
Class-Path: commons-logging-1.1.jar commons-lang.jar antlr.jar toplink
 .jar GroboTestingJUnit-1.2.1-core.jar junit.jar

这让我以及我已经烦扰了一天多的任何同事都感到难过。只是为了挑选答案,至少目前,由于许可限制和公司政策(例如:用于创建 exe 或打包 jar 的工具),对此的第三方解决方案可能已经出现。最终目标是创建一个 jar,它可以从我的开发 Windows 机器复制到 Linux 服务器(带有任何依赖的 jar)并用于填充数据库(因此类路径可能最终在开发和部署环境之间有所不同)。任何有关此谜团的线索将不胜感激!

【问题讨论】:

    标签: java jar classpath executable-jar


    【解决方案1】:

    -jar 选项与-classpath 互斥。查看旧描述here

    -罐子

    执行封装在 JAR 文件中的程序。第一个参数是 JAR 文件的名称,而不是启动类名称。为了使该选项起作用,JAR 文件的清单必须包含格式为 Main-Class: classname 的行。在这里,classname 标识了具有 public static void main(String[] args) 方法的类,该方法用作应用程序的起点。

    有关使用 Jar 文件和 Jar 文件清单的信息,请参阅 Jar 工具参考页面和 Java 教程的 Jar 跟踪。

    使用此选项时,JAR 文件是所有用户类的来源,其他用户类路径设置将被忽略。

    一个快速而肮脏的技巧是将您的类路径附加到引导类路径:

    -Xbootclasspath/a:路径

    指定以冒号分隔的directires、JAR 档案和ZIP 档案路径以附加到默认引导类路径。

    但是,正如@Dan 所说,正确的解决方案是确保您的 JAR 清单包含它需要的所有 JAR 的类路径。

    【讨论】:

    • 最好在 JAR 的清单中指定一个类路径。 bootclasspath 用于替换系统类。
    • True - 将对其进行编辑以明确这只是一种解决方法。
    • 啊哈!而已!我知道一定有一些简单的东西我错过了。只是这里没有人使用罐子足以注意到我的错误!
    • 不幸的是,在从一个系统部署到另一个系统时,将完整路径放在清单中似乎充满了危险。这些罐子不一定会在两个系统上位于同一个地方。
    【解决方案2】:

    如果您在程序中使用外部库并尝试将所有内容打包到一个 jar 文件中,那么由于类路径问题等原因,这并不是那么简单。

    对于这个问题,我更愿意使用OneJar

    【讨论】:

    • 我看了一点 OneJar。这可能是一种可能性——许可证似乎很简单。但我仍然必须在我工作的地方获得“批准”才能使用它。
    【解决方案3】:

    您可以省略 -jar 选项并像这样启动 jar 文件:

    java -cp MyJar.jar;C:\externalJars\* mainpackage.MyMainClass

    【讨论】:

    • 谢谢,您让我免于重新编译一个忘记包含其依赖项的大型 jar。网络上的其他人一直在说您不能同时使用 -classpath-jar,但您显然正在这样做。
    • 天哪,这太棒了。谢谢!
    • 澄清一下,这里没有同时使用 -jar 选项和 -classpath 选项。相反,-classpath 选项接受 jar 和通配符: -cp -classpath A:分隔的目录列表、JAR 档案和用于搜索类文件的 ZIP 存档。
    • 在我绞尽脑汁之后发现了这个。谢谢!
    • 谢谢。就我而言,我必须将路径用双引号括起来,否则它不起作用。示例:java -cp "lib\jcommander-1.81.jar;bin" com.aderchox.App --length 512 -p 2
    【解决方案4】:

    我发现当我使用清单时,类路径的 jar 列表需要在每个 jar 列表之后有一个空格,例如“required_lib/sun/pop3.jar required_lib/sun/smtp.jar”。即使它是列表中的最后一个。

    【讨论】:

      【解决方案5】:

      这是正在发生的问题,

      如果 JAR 文件是从“C:\java\apps\appli.jar”加载的,并且您的清单文件具有 Class-Path: 引用“lib/other.jar”,则类加载器将在“C :\java\apps\lib\" 用于“other.jar”。它不会查看 JAR 文件条目“lib/other.jar”。

      解决方案:-

      1. 右键单击项目,选择导出。
      2. 选择 Java 文件夹并在其中选择 Runnable JAR File 而不是 JAR 文件。
      3. 选择正确的选项并在库处理部分选择第三个选项,即(将所需的库复制到生成的 JAR 旁边的子文件夹中)。

      [ EDIT = 第三个选项除了 jar 之外还生成一个文件夹,第二个选项(“将所需的库打包到生成的 JAR”)也可以在您拥有 jar 时使用。 ]

      1. 单击完成,您的 JAR 将在指定位置创建,同时创建一个文件夹,其中包含清单文件中提到的 JARS。
      2. 打开终端,为你的 jar 提供正确的路径并使用这个命令 java -jar abc.jar 运行它

        现在会发生的是类加载器将在正确的文件夹中查找引用的 JARS,因为现在它们存在于包含您的应用程序 JAR 的同一文件夹中。现在没有抛出“java.lang.NoClassDefFoundError”异常.

      这对我有用...希望它也对你有用!!!

      【讨论】:

      • 你的答案是正确的,但是使用第三个选项它会生成一个除了 jar 之外的文件夹,我认为第二个选项(“将所需的库打包到生成的 JAR”)是一个更好的选择,因为你只有罐子。不过,你有我的投票;)
      【解决方案6】:

      我的 jar 也有同样的问题 解决办法

      1. 创建 MANIFEST.MF 文件:

      清单版本:1.0

      密封:真

      类路径:. lib/jarX1.jar lib/jarX2.jar lib/jarX3.jar

      主类:com.MainClass

      1. 右键单击项目,选择导出。

      选择导出选中项目的所有输出文件夹

      1. 选择使用工作区中的现有清单并选择 MANIFEST.MF 文件

      这对我有用:)

      【讨论】:

        猜你喜欢
        • 2013-05-19
        • 2021-09-28
        • 2014-10-24
        • 1970-01-01
        • 2010-11-28
        • 1970-01-01
        • 1970-01-01
        • 2013-04-26
        • 1970-01-01
        相关资源
        最近更新 更多