JFace FontRegistry上的NoClassDefFoundError

时间:2009-02-27 22:55:30

标签: java eclipse swt

当我启动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. 这是 a ClassNotFoundException。该类位于类路径中。如果我将源附加到jar,我可以调试到getFontRegistry方法。该方法将成功执行多次,最后在第338行抛出NoClassDefFoundError。第337行是“if variable == null”语句,检查静态变量是否已初始化。第338行正在初始化它,如果它尚未初始化。第一次通过时,空检查失败,并执行初始化。在随后的方法传递中,null检查通过,因此返回已初始化的静态值。在最后一次传递(失败的那个)上,null检查再次失败(即使静态变量已经初始化),当它尝试重新初始化静态变量时,抛出NoClassDefFoundError。这是相关的源(从第336行开始,请注意,fontRegistry是一个私有的静态变量,在其他任何地方都没有设置):
  4. public static FontRegistry getFontRegistry() {
       if (fontRegistry == null) {
         fontRegistry = new FontRegistry(
             "org.eclipse.jface.resource.jfacefonts");
       }
       return fontRegistry;
    }
    

    1. 我已经获得了jar的新副本(以确保它没有被破坏),删除了我的.classpath和.project文件并启动了一个新项目,并重新创建了启动配置文件。没有变化。
    2. 由于上面#3中的特殊性,我怀疑某种奇怪的类加载器行为 - 似乎最后一次通过该方法是在另一个类加载器中?

      想法?

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

5 个答案:

答案 0 :(得分:3)

听起来你错过了一个持有依赖关系的JAR文件,正如2006年7月的博客文章中所提到的那样,由Sanjiv JIVAN撰写:

Difference between ClassNotFoundException and NoClassDefFoundError

  

ClassLoader找不到报告的类时,会抛出 ClassNotFoundException
  这通常意味着CLASSPATH中缺少班级   这也可能意味着有问题的类正在尝试从父类ClassLoader中加载的另一个类加载,因此子ClassLoader中的类不可见。
  在像App Server这样的更复杂的环境中工作时有时会出现这种情况(WebSphere对于此类ClassLoader问题很臭名昭着。)

     

人们往往会将 java.lang.NoClassDefFoundError java.lang.ClassNotFoundException混为一谈。然而,有一个重要的区别。

     

例如,异常(错误,因为 java.lang.NoClassDefFoundError java.lang.Error 的子类),例如

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

并不意味着ActiveMQConnectionFactory类不在CLASSPATH中。

     事实上,它恰恰相反。

     

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

     

当有问题的类具有静态块或使用ClassLoader未找到的类的成员时,通常会发生这种情况。

     

因此,要查找罪魁祸首,请查看相关课程的来源(本例中为ActiveMQConnectionFactory),使用静态块或静态成员查找代码
  如果您无权访问源代码,则只需使用 JAD 对其进行反编译。

     

在检查代码时,假设您找到如下所示的代码行,请确保CLASSPATH中的类SomeClass。

private static SomeClass foo = new SomeClass();
  

提示:要了解某个类属于哪个jar,您可以使用网站 jarFinder 。这允许您使用通配符指定类名,并在其数据库中搜索该类   jarhoo允许您执行相同的操作,但不再可以自由使用。

     

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

答案 1 :(得分:2)

我认为上面介绍的堆栈跟踪隐藏了真正的问题。以下是方法run中的代码 org.eclipse.jface.dialogs.ProgressMonitorDialog(我添加评论):

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块中的代码也会抛出异常,因此原始异常将丢失。

答案 2 :(得分: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。你能报告一下这发生了什么吗?根据结果​​,我可能会有更多的想法。

答案 3 :(得分:1)

要添加到excellent TofuBeer's answer,因为NoClassDefFoundError表示:

    {li>找到了 org.eclipse.jface.resource.FontRegistry ClassLoader
  • 但是无法在不触发错误的情况下加载,例如使用静态块或使用Class 找不到的ClassLoader的成员。

让我们看一下org.eclipse.jface.resource.FontRegistry source code

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

让我们看一下 org.eclipse.jface.resource.JFaceResources source code

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

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

因此, begs 引发 question:为什么static initialized variable会突然被视为null

因为不知怎的FontRegistry or JFaceResources get unloaded by the gc?!

  

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

     

因此,无论该类的实例是否随时存在都无关紧要,只要已经加载了Class本身,该字段就会存在。


如果这是一个 eclipse插件,这可能与此 FAQ entry

有关
  

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

     

不要开始查看运行时工作台的启动配置中的Plug-ins and Fragments选项卡   该选项卡仅影响用于运行时工作台的插件,以及它们是从工作空间还是从Eclipse安装目录加载的。

     

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

     

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

答案 4 :(得分:1)

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

org.eclipse.core.commands_xxxxx.jar

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