尝试使用java.exe -jar运行我的jar文件时出现NoClassDefFoundError错误...出了什么问题?

72

我有一个应用程序,我想把它打成一个jar包以便更容易部署。当我将这个应用程序编译成一组从CLASSPATH可达的类并在Windows cmd窗口中运行时,它可以正常编译和运行。但是,当我将我的类打成jar包,并尝试在同一个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)的内容:

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文件的工具),第三方解决方案可能不适用。最终目标是创建一个可以从我的开发Windows计算机复制到Linux服务器上的jar文件(以及任何依赖的jar文件),并用它来填充数据库(因此在开发和部署环境之间可能存在不同的类路径)。如果您有任何线索,将不胜感激!

7个回答

64
-jar选项与-classpath选项互斥。在此处可查看旧版描述 here
-jar 从 JAR 文件中执行一个程序。第一个参数是 JAR 文件的名称,而不是启动类名。为了使此选项工作,JAR 文件的清单必须包含一行形式为 Main-Class: classname 的行。这里,classname 标识具有 public static void main(String[] args) 方法的类,该方法作为应用程序的起始点。
有关使用 Jar 文件和 Jar 文件清单的信息,请参见 Jar 工具参考页面和 Java 教程的 Jar 路径。
当您使用此选项时,JAR 文件是所有用户类的源,并忽略其他用户类路径设置。
一种简单粗暴的解决方案是将classpath追加到引导类路径:
-Xbootclasspath/a:path 指定要附加到默认引导类路径的目录、JAR 归档文件和 ZIP 归档文件的以冒号分隔的路径。
然而,正如 @Dan 所说,正确的解决方案是确保您的 JAR 的清单包含它所需的所有 JAR 的类路径。

2
最好在JAR的清单中指定一个类路径。引导类路径是用于替换系统类的。 - Dan Dyer
True -- 这只是一个解决方法,需要进行修改以确保清晰明确。 - toolkit
啊哈!就是这样!我知道一定有什么简单的东西我没注意到。只是这里没有人用过jar文件,所以没注意到我的错误! - Ogre Psalm33
3
很遗憾,将完整路径放入清单中在从一个系统部署到另一个系统时似乎充满了危险。JAR文件在两个系统上的位置不一定相同。 - Ogre Psalm33

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

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


2
谢谢,你救了我一命!巨大的 jar 包忘记包含它的依赖关系,我本来还要重新编译一遍。网上其他人都说你不能同时使用“-classpath”和“-jar”,但你显然在做这个操作。 - kanzure
2
天啊,这太棒了。谢谢! - Qix - MONICA WAS MISTREATED
2
仅澄清一下,在此处不会同时使用-jar选项和-classpath选项。相反,-classpath选项接受jar和通配符:-cp <目录和zip/jar文件的类搜索路径> -classpath <目录和zip/jar文件的类搜索路径> 由冒号分隔的目录、JAR档案和ZIP档案列表, 用于搜索类文件。 - Hazok
在我绞尽脑汁之后,我找到了这个。谢谢! - kopos
1
谢谢。在我的情况下,我必须用双引号将路径包装起来,否则它不起作用。例子:java -cp "lib\jcommander-1.81.jar;bin" com.aderchox.App --length 512 -p 2 - aderchox

21

这是目前出现的问题,

如果JAR文件从"C:\java\apps\appli.jar"加载,并且您的清单文件引用了"lib/other.jar",则类加载器将在"C:\java\apps\lib\"中查找"other.jar"。它不会查看JAR文件条目"lib/other.jar"。

解决方法:

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

[编辑= 第3个选项会生成一个文件夹以及jar文件,也可以使用第2个选项(“将所需库打包到生成的JAR中”),因为您已经有jar文件。]

  1. 单击完成,你的JAR文件将被创建在指定位置,同时包含了manifest文件中所提到的JARS的文件夹。
  2. 打开终端,给予正确的路径到您的jar文件并使用以下命令运行它 java -jar abc.jar

    现在会发生的情况是由于现在它们存在于包含应用程序JAR的相同文件夹中,类加载器将在正确的文件夹中查找所引用的JARS。现在不会抛出“java.lang.NoClassDefFoundError”异常。

这个方法对我有用... 希望对你也起作用!!!


你的答案是正确的,但第三个选项会生成一个文件夹和jar包,我认为第二个选项更好(“将所需库打包到生成的JAR中”),因为你只需要jar包。不过,你有我的投票 ;) - maxivis

3

我也遇到了关于jar文件的问题,以下是解决方法:

  1. 创建MANIFEST.MF文件:

Manifest-Version: 1.0

Sealed: true

Class-Path: . lib/jarX1.jar lib/jarX2.jar lib/jarX3.jar

Main-Class: com.MainClass

  1. 右键单击项目,选择“Export”。

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

  1. 选择“使用工作区中现有的清单”,然后选择MANIFEST.MF文件

这对我有效 :)


3
如果您在程序中使用外部库,并尝试将所有内容打包成一个jar文件,这并不简单,因为存在类路径等问题。
我建议使用OneJar来解决这个问题。

我稍微看了一下OneJar。这可能是一个选择 - 许可证似乎很简单明了。但我仍然需要在我的工作中“获得批准”才能使用它。 - Ogre Psalm33

0

我发现当我使用清单(manifest)时,类路径(classpath)的jar列表需要在每个jar的列表后面加一个空格,例如"required_lib/sun/pop3.jar required_lib/sun/smtp.jar"。即使它是列表中的最后一个。


0
在g_tom的解决方案的基础上,我发现为运行jar文件编写一个批处理文件会使事情变得更容易。
:: To run MyClass.jar, name this file MyClass.bat and place it 
:: in the same directory as MyClass.jar and any dependent jars
@echo off 
setlocal enableextensions
setlocal enabledelayedexpansion
set BAT_FILE=%~n0
cd %~dp0
java -cp "!BAT_FILE:.bat=!.jar;.\*" !BAT_FILE:.bat=! %*

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接