3

当我启动 SWT 应用程序(通过 Eclipse 启动配置文件)时,我收到以下堆栈跟踪:

Exception in thread "main" java.lang.NoClassDefFoundError: org/eclipse/jface/resource/FontRegistry
    at org.eclipse.jface.resource.JFaceResources.getFontRegistry(JFaceResources.java:338)
    at org.eclipse.jface.window.Window.close(Window.java:313)
    at org.eclipse.jface.dialogs.Dialog.close(Dialog.java:971)
    at org.eclipse.jface.dialogs.ProgressMonitorDialog.close(ProgressMonitorDialog.java:348)
    at org.eclipse.jface.dialogs.ProgressMonitorDialog.finishedRun(ProgressMonitorDialog.java:582)
    at org.eclipse.jface.dialogs.ProgressMonitorDialog.run(ProgressMonitorDialog.java:498)
    at com.blah.si.workflow.SWTApplication.main(SWTApplication.java:135)

现在,让这变得奇怪的事情:

  1. 当我更改项目构建路径并将 jface.jar 替换为源项目(相同版本 - 3.3.1)时,错误消失了。
  2. 我拥有的其他应用程序使用相同的 jar,以及相同启动配置文件和项目的副本,一切正常。
  3. 不是一个ClassNotFoundException. 该类位于类路径上。如果我将源附加到 jar,我可以调试到 getFontRegistry 方法。该方法将成功执行几次,然后最终NoClassDefFoundError在第 338 行抛出一个。第 337 行是一个“if variable == null”语句,用于检查静态变量是否已被初始化。如果它尚未初始化,第 338 行正在初始化它。第一次通过,空检查失败,进行初始化。在随后通过该方法时,空检查通过,因此返回已经初始化的静态值。在最后一次通过(失败的那一次)时,空值检查再次失败(即使静态变量已经初始化),当它尝试重新初始化静态变量时,NoClassDefFoundError被抛出。这是相关的源代码(从第 336 行开始,注意 fontRegistry 是一个私有静态变量,没有在其他地方设置):

.

public static FontRegistry getFontRegistry() {
   if (fontRegistry == null) {
     fontRegistry = new FontRegistry(
         "org.eclipse.jface.resource.jfacefonts");
   }
   return fontRegistry;
}

.

  1. 我已经获得了 jar 的新副本(以确保它没有损坏),删除了我的 .classpath 和 .project 文件并开始了一个新项目,并重新创建了启动配置文件。不用找了。

由于上面 #3 中的特殊性,我怀疑某种奇怪的类加载器行为 - 似乎最终通过该方法的方法是在另一个类加载器中?

想法?

更新: Pourquoi Litytestdata 提供的答案提示我注意在 ProgressMonitorDialog 的第 458 行上方的 try 块中发生了什么。实际上,该代码正在引发异常,该异常正在被 finally 块吞噬。根本原因是另一个缺失的类(缺失的类不是 JFontRegistry 或其任何直接相关的类,而是另一个在边缘情况下依赖于蜘蛛网的类。)我赞成所有答案,指出我要注意类路径,并接受 Pourquoi 的,因为这是突破。谢谢大家。

4

5 回答 5

3

听起来您缺少一个包含依赖项的 JAR 文件,如Sanjiv JIVAN于 2006 年 7 月的这篇博客文章中所述:

ClassNotFoundException 和 NoClassDefFoundError 之间的区别

ClassNotFoundException当 ClassLoader 找不到报告的类时抛出A。
这通常意味着该类从CLASSPATH.
这也可能意味着有问题的类正试图从另一个类中加载,该类在父类中加载,ClassLoader因此来自子类的类ClassLoader不可见。
在应用服务器等更复杂的环境中工作时有时会出现这种情况(WebSphere 因此类ClassLoader问题而臭名昭著)。

人们往往容易java.lang.NoClassDefFoundErrorjava.lang.ClassNotFoundException. 但是有一个重要的区别。

例如一个异常(一个错误,因为java.lang.NoClassDefFoundError它是 的子类java.lang.Error),比如

java.lang.NoClassDefFoundError:
org/apache/activemq/ActiveMQConnectionFactory

并不意味着 ActiveMQConnectionFactory 类不在CLASSPATH.

事实上,情况恰恰相反。

这意味着该类ActiveMQConnectionFactory已被找到,ClassLoader但是在尝试加载该类时,它在读取类定义时遇到了错误。

这通常发生在所讨论的类具有使用ClassLoader.

因此,要找到罪魁祸首,请查看相关类的源代码(ActiveMQConnectionFactory在这种情况下)并使用静态块或静态成员查找代码
如果您无权访问源代码,则只需使用JAD对其进行反编译。

在检查代码时,假设您找到如下代码行,请确保您的CLASSPATH.

private static SomeClass foo = new SomeClass();

提示:要找出一个类属于哪个 jar,您可以使用网站jarFinder。这允许您使用通配符指定类名,并在其 jar 数据库中搜索该类。
jarhoo允许你做同样的事情,但它不再免费使用。

如果您想在本地路径中找到一个类所属的 jar,您可以使用jarscan 之类的实用程序。您只需指定要查找的类以及您希望它开始在 jars 和 zip 文件中搜索类的根目录路径。

于 2009-02-28T09:18:53.687 回答
2

我认为上面介绍的堆栈跟踪在这里隐藏了真正的问题。以下是org.eclipse.jface.dialogs.ProgressMonitorDialogrun中方法中 的代码(我添加了注释):

public void run(boolean fork, boolean cancelable,
         IRunnableWithProgress runnable) throws InvocationTargetException,
         InterruptedException {
     setCancelable(cancelable);
     try {
         aboutToRun();
         // Let the progress monitor know if they need to update in UI Thread
         progressMonitor.forked = fork;
         ModalContext.run(runnable, fork, getProgressMonitor(), getShell()
                 .getDisplay());
     } finally {
         finishedRun();  // this is line 498
     }
}

Jared 的堆栈跟踪中倒数第二行是此类的第 498 行,它是对块finishedRun()内的调用。finally我怀疑真正的原因是在try块中引发了异常。由于finally块中的代码也抛出了异常,所以原来的异常丢失了。

于 2009-02-28T13:11:01.820 回答
2

为了更好地处理它是否是类加载器问题,请查看它工作的代码并添加:

try
{
    final Class       clazz;
    final ClassLoader loader;

    clazz  = Class.forName("org/eclipse/jface/resource/FontRegistry");
    loader = clazz.getClassLoader(); 
    System.out.println("The classloader at step 1 is: " + loader);
}
catch(final Throwable ex)
{
    ex.printStackTrace();
}

然后在你得到 NoClassDefFoundError 的地方做同样的事情,看看类加载器是否不同。

然后您将能够确保它是不同的 ClassLoader。你能报告一下这会发生什么吗?根据结果​​,我可能会有更多想法。

于 2009-03-02T02:52:36.407 回答
1

添加到优秀的 TofuBeer 的答案中,因为NoClassDefFoundError表明:

  • 班级, _org.eclipse.jface.resource.FontRegistry ClassLoader
  • 但不能在不触发错误情况下加载,例如使用ClassClassLoader.

让我们看一下org.eclipse.jface.resource.FontRegistry源代码

它没有任何静态变量初始化(也没有它的超类)。

让我们看看org.eclipse.jface.resource.JFaceResources源代码

触发错误的getFontRegistry()函数是使用静态变量 fontRegistry

/**
 * The JFace font registry; <code>null</code> until lazily initialized or
 * explicitly set.
 */
private static FontRegistry fontRegistry = null;

因此,它引出 一个问题:为什么会突然再次考虑静态初始化变量null

因为不知何故FontRegistry或被JFaceResourcesgc 卸载了?!

如果一个字段被声明为静态的,那么无论最终创建多少个类的实例(可能为零),都只存在该字段的一个化身。静态字段,有时称为类变量,在类初始化时体现(第 12.4 节)。

所以无论类的实例是否在任何时候都存在,只要类本身已经加载,该字段就会存在。


如果这是一个eclipse Plugin,这可能与此FAQ 条目有关

这是一个新用户的典型场景:
您正在编写一个扩展插件 XYZ 的插件。
要编译它,您可以从 Java Build Path 属性页面或通过编辑 .classpath 文件,将插件 XYZ 的 JAR 文件的引用添加到项目的构建路径中。
当您启动运行时工作台时,会报告以下令人惊讶的错误:java.lang.NoClassDefFoundError: XYZ.SomeClass.

不要开始查看运行时工作台的启动配置中的插件和片段选项卡。
该选项卡仅影响用于运行时工作台的插件以及它们是从工作区加载还是从 Eclipse 安装目录加载。

相反,开始查看插件清单。
编辑 plugin.xml 文件并确保提到 XYZ 作为必需的插件
然后,保存 plugin.xml 文件。
这将自动更新项目的构建路径。

编写插件时切勿手动编辑 .classpath 文件。
插件清单编辑器只会覆盖您对其所做的任何更改。不是很文明,但这就是它的工作方式。

于 2009-02-28T00:38:08.447 回答
1

如果您尝试自己加载类 FontRegistry(如 TofoBeer 所述),您会发现如果使用 FontRegistry,则以下 JAR 的类是依赖类。

org.eclipse.core.commands_xxxxx.jar

您必须将此 JAR 添加到您的构建路径中。

于 2011-09-20T11:46:15.023 回答