2017-02-28 59 views
8

我們有一個桌面Swing應用程序和Google Guice 4.1.0依賴注入。在開發過程中一切正常,但當同事嘗試運行應用程序時發生了一些奇怪的事情。Guice無法實例化擴展JPanel的類 - NPE調用超級構造函數

我們有一個MainWindow類延伸JPanel。在構造函數中,這個類需要一些本身是可注入的控制器。在主要方法Guice注射器創建。然後噴油器試圖實例化MainWindowinjector.getInstance(MainWindow.class))。它失敗了NullPointerException

這不會發生在我的電腦上,我們使用相同的JDK。

下面是MainWindow類剝離下來到有問題的代碼(注意:這不會重現該問題,不幸):

class MainWindow extends JPanel { 
    private final Foo foo; 

    private final JFrame frame; 

    @Inject 
    public MainWindow(Foo foo) { 
     super(new GridBagLayout()); // <-- NullPointerException 
     this.foo = foo; 
     this.frame = new JFrame("title"); 
    } 

    public void createAndShowGUI() { 
     // ... 
     frame.add(this); 
     frame.pack(); 
     frame.setVisible(true); 
    } 
} 

這裏是main()方法:

class Main { 
    private static final Injector injector = Guice.createInjector(); 

    public static void main(String[] args) { 
     MainWindow mainWindow = injector.getInstance(MainWindow.class); 

     SwingUtilities.invokeLater(new Runnable() { 
      public void run() { 
       mainWindow.createAndShowGUI(); 
      } 
     }); 
    } 
} 

下面是堆棧跟蹤的例外情況:

com.google.inject.ProvisionException: Unable to provision, see the following errors: 

1) Error injecting constructor, java.lang.NullPointerException 
    at app.gui.MainWindow.<init>(MainWindow.java:133) 
    while locating app.gui.MainWindow 

1 error 
     at com.google.inject.internal.InjectorImpl$2.get(InjectorImpl.java:1028) ~[app-1.0-SNAPSHOT.jar:?] 
     at com.google.inject.internal.InjectorImpl.getInstance(InjectorImpl.java:1054) ~[app-1.0-SNAPSHOT.jar:?] 
     at app.Main.createAndShowGUI(Main.java:40) ~[app-1.0-SNAPSHOT.jar:?] 
     at app.Main.access$000(Main.java:26) ~[app-1.0-SNAPSHOT.jar:?] 
     at app.Main$2.run(Main.java:67) ~[app-1.0-SNAPSHOT.jar:?] 

NPE被引入了最令人驚訝的地方 - 在調用MainWindow的超類構造函數(這是133行)。我開始挖掘,發現,手動創建MainWindow並注入其依賴正常工作:

MainWindow mainWindow = new MainWindow(injector.getInstance(Foo.class)); 

我懷疑,也許類加載器不能正常工作,所以我既MainWindowJPanel的日誌記錄的類加載器再次嘗試:

System.out.println("MainWindow: " + MainWindow.class.getClassLoader()); 
System.out.println("JPanel:  " + JPanel.class.getClassLoader()); 
MainWindow mainWindow = injector.getInstance(MainWindow.class); 

類加載器是不同的(JPanel通過引導加載),但現在的噴射工作正常。我想這是因爲現在JPanel類被顯式加載到main方法上下文中。

所以我的問題是:

  1. 沒有人有類似的問題?
  2. 這是我的錯,還是它的一個錯誤?
  3. 如果這是一個bug,它發生在Guice嗎?或者也許JRE?

更多關於Java和操作系統的詳細信息:

  • 我最初JDK 1.8.0u111開發的,但後來改JDK 1.8.0u121。
  • 應用程序被編譯爲Java 6.
  • 在我的電腦上運行完美的Windows 10版本1607(操作系統版本14393.693),在JRE 6和JRE 8(來自JDK)。
  • NullPointerException是在同事的電腦上用Windows 10,1511(OS Build 10586.753),JDK 1.8.0u112和1.8.0u121提出的。

不幸的是,我無法提供重現問題的最小版本。哎呀,我甚至不能重現問題,它只發生在同事的環境中。

+0

你看過Guice配置文件嗎?你和同事之間有什麼分別嗎? –

+0

@ M.Prokhorov沒有區別。項目是從Maven構建的,不需要添加額外的配置。更何況,這是一個運行時失敗,而不是編譯。完全相同的JAR適用於我的電腦,但不適用於他。不管它是由我還是他編譯的都沒關係。 – Archie

+0

那麼,如果類加載器不同,那麼它肯定是一個運行時問題。這兩臺機器都有不同的類加載器,由該日誌語句報告?噴射器被調用時是否有任何打印? –

回答

1

我高度懷疑這是由於競爭條件。 Swing組件不是線程安全的,並且應在EDT實例化爲每swing package javadoc

Swing的線程策略

一般Swing不是線程安全的。除非另有說明,否則所有Swing組件和相關的 類必須在事件 調度線程上訪問。典型的Swing應用程序在 響應從用戶手勢產生的事件處理。例如,點擊JButton的 會通知所有添加到JButton的ActionListeners。由於用戶手勢生成的所有事件均在 事件派發線程上調度,因此大多數開發人員不受 限制的影響。

但是,其影響在於構建和展示Swing 應用程序。調用應用程序的主要方法或 Applet中的方法不會在事件派發線程上調用。 因此,當 構造並顯示應用程序或小應用程序時,必須小心 以將控制轉移到事件派發線程。轉移控件並開始使用Swing的首選方式是 ,其使用方法是使用 invokeLater。 invokeLater方法將一個Runnable調度爲在事件派發線程上處理的 。

(重點煤礦)

現在你開始在美國東部時間的UI,使用invokeLater,但是你構建主線程的UI(通過吉斯注射器調用)。 Guice噴射器調用也應該在invokeLater部分啓動UI。

相關問題