簡體   English   中英

JFace FontRegistry上的NoClassDefFoundError

[英]NoClassDefFoundError on JFace FontRegistry

當我啟動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方法。 該方法將成功執行幾次,最后在第338行拋出NoClassDefFoundError 。第337行是“if variable == null”語句,檢查是否已初始化靜態變量。 第338行正在初始化它,如果它尚未初始化。 第一次通過時,空檢查失敗,並執行初始化。 在隨后的方法傳遞中,null檢查通過,因此返回已初始化的靜態值。 在最后一次傳遞(失敗的那個)上,null檢查再次失敗(即使靜態變量已經初始化),當它嘗試重新初始化靜態變量時,拋出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的,因為這是突破。 謝謝大家。

聽起來你錯過了一個擁有依賴關系的JAR文件,正如Sanjiv JIVAN撰寫的2006年7月的博客文章所述

ClassNotFoundException和NoClassDefFoundError之間的區別

當ClassLoader找不到報告的類時,拋出ClassNotFoundException
這通常意味着CLASSPATH缺少該類。
它也可能意味着有問題的類試圖從另一個加載在父ClassLoader的類ClassLoader ,因此子ClassLoader是不可見的。
在像App Server這樣的更復雜的環境中工作時有時會出現這種情況(WebSphere對於此類ClassLoader問題很臭名昭着)。

人們經常傾向於將java.lang.NoClassDefFoundErrorjava.lang.ClassNotFoundException混淆。 然而,有一個重要的區別。

例如異常(真因為錯誤java.lang.NoClassDefFoundError是的一個子類java.lang.Error )像

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

並不意味着ActiveMQConnectionFactory類不在CLASSPATH

事實上,它恰恰相反。

這意味着ClassLoader找到了類ActiveMQConnectionFactory ,但是在嘗試加載類時,它會在讀取類定義時遇到錯誤。

當有問題的類具有靜態塊或使用ClassLoader未找到的ClassLoader成員時,通常會發生這種情況。

因此,要查找罪魁禍首,請查看相關類的源(本例中 ActiveMQConnectionFactory ),並使用靜態塊或靜態成員查找代碼
如果您沒有訪問源代碼,那么只需使用JAD對其進行反編譯即可。

在檢查代碼時,假設您找到了如下所示的代碼行,請確保CLASSPATH中的類SomeClass。

private static SomeClass foo = new SomeClass();

提示:要找出類所屬的jar,可以使用網站jarFinder 這允許您使用通配符指定類名,並在其數據庫中搜索類的jar。
jarhoo允許你做同樣的事情,但它不再免費使用。

如果您想在本地路徑中找到類所屬的jar,可以使用jarscan之類的實用程序。 您只需指定要查找的類以及您希望在其中開始在jar和zip文件中搜索類的根目錄路徑。

我認為上面提到的堆棧跟蹤隱藏了真正的問題。 下面是在org.eclipse.jface.dialogs.ProgressMonitorDialog中 run的方法中的代碼(我添加了注釋):

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的stacktrace中的第二個底線是這個類的第498行,它是finally塊中對finishedRun()的調用。 我懷疑真正的原因是在try塊中拋出異常。 由於finally塊中的代碼也會拋出異常,因此原始異常將丟失。

為了更好地處理它是否是一個類加載器問題,請查看它工作的代碼並添加:

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。 你能報告一下這發生了什么嗎? 根據結果​​,我可能會有更多的想法。

添加到優秀的TofuBeer的答案 ,因為NoClassDefFoundError指示:

  • ClassLoader 發現了類org.eclipse.jface.resource.FontRegistry
  • 但是如果沒有觸發錯誤就無法加載,比如使用靜態塊或使用ClassLoader找不到的Class成員

我們來看看org.eclipse.jface.resource.FontRegistry源代碼

它沒有任何靜態變量初始化(也不是它的超類)。

我們來看看org.eclipse.jface.resource.JFaceResources源代碼

觸發Error的getFontRegistry()函數使用靜態變量fontRegistry

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

因此,它 引發 一個問題 :為什么靜態初始化變量會突然被認為是null

因為某種方式FontRegistryJFaceResources被gc卸載了 ?!

如果一個字段被聲明為static,那么無論該類最終可以創建多少個實例(可能為零),都只存在該字段的一個化身。 初始化類時,靜態字段(有時稱為類變量)會顯示(第12.4節)。

因此,無論該類的實例是否隨時存在都無關緊要,只要已經加載了Class本身,該字段就會存在。


如果這是一個eclipse插件 ,這可能與此FAQ條目有關

以下是新用戶的典型方案:
您正在編寫一個擴展插件XYZ的插件。
要使其編譯,可以從Java Build Path屬性頁或通過編輯.classpath文件向項目的構建路徑添加對插件XYZ的JAR文件的引用。
啟動運行時工作台時,會報告以下令人驚訝的錯誤: java.lang.NoClassDefFoundError: XYZ.SomeClass

不要開始查看運行時工作台的啟動配置中的Plug-ins and Fragments選項卡。
該選項卡僅影響用於運行時工作台的插件,以及它們是從工作空間還是從Eclipse安裝目錄加載的。

相反,開始查看插件清單。
編輯plugin.xml文件並確保將XYZ作為必需的插件提及
然后,保存plugin.xml文件。
這將自動更新項目的構建路徑。

在編寫插件時,切勿手動編輯.classpath文件。
插件清單編輯器只會覆蓋您對其所做的任何更改。 不是很文明,但這就是它的工作方式。

如果您嘗試自己加載FontRegistry類(如TofoBeer所描述),您將發現如果使用FontRegistry,以下JAR的類是依賴類。

org.eclipse.core.commands_xxxxx.jar

您必須將此JAR添加到構建路徑。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM