NetBeans 平台的主要優點是其模塊化體系結構。其他優點還有 NetBeans 平台所依賴的 Swing UI 工 具包(使用 Java 創建用戶界面的正式工具包)以及 NetBeans IDE 屢獲殊榮的 "Matisse" GUI 生成器 。
在本快速入門教程中,我們將通過一個非常簡單的示例向您介紹模塊化的優點和使用情況,此示例由 目前就讀於奧地利林茨市 Johannes Kepler 大學的博士生 Thomas Würthinger 提供。在了解本快速入門 教程中所介紹的概念後,您便可以參閱 NetBeans 平台學習資源,其中為許多與 NetBeans 平台相關的不 同方案提供了各種非常豐富的教程。
如果您不熟悉 NetBeans 平台,強烈建議您觀看截屏視頻系列 Top 10 NetBeans APIs(最主要的 10 個 NetBeans API)。
目錄
包含單個模塊的 NetBeans 平台應用程序
使用 Lookup 的模塊化應用程序
LookupListener 和 InstanceContent
要學習本教程,您需要具備下表中列出的軟件和資源。
注意:雖然 NetBeans 平台是一個獨立的產品,但無需單獨下載該平台。通常, 您可以在 NetBeans IDE 中開發應用程序,然後排除特定於 IDE 但對應用程序來說是多余的模塊。
包含單個模塊的 NetBeans 平台應用程序
首先,我們只是創建一個包含單個模塊的新 NetBeans 平台應用程序。
選擇“文件”|“新建項目”,然後選擇“NetBeans 模塊”。選擇“NetBeans 平台應用程序”。您應 看到如下所示的內容:
單擊“下一步”。
將新應用程序命名為 "WordApp",並使其成為 IDE 的主項目:
當調用全局項目命令“運行項目”時,將啟動 IDE 主項目。
單擊“完成”。
在“項目”窗口中展開新應用程序,右鍵單擊“模塊”節點,然後選擇“添加新模塊”。將新模塊命 名為 "WordEngine":
單擊“下一步”。
為模塊指定一個唯一的名稱(即其代碼名稱基)以及將顯示在“項目”窗口中的名稱。
單擊“完成”。將創建新模塊,並在“項目”窗口中顯示其結構。
右鍵單擊 "WordEngine" 模塊,然後選擇“新建”|“其他”。在“模塊開發”類別中,選擇“窗口組 件”:
單擊“下一步”。現在,您應看到如下所示的內容:
單擊“下一步”。
將“類名前綴”設置為 "Text",並將“包”設置為 "org.demo.wordengine"。
單擊“完成”。在模塊的源結構中添加新窗口以及一些附屬 XML 文件。
現在,雙擊 "TextTopComponent.java" 文件,以在 "Matisse" GUI 生成器的“設計”視圖中將其打 開。使用“組件面板”(Ctrl-Shift-8) 將一個按鈕和一個文本區域拖放到窗口中:
右鍵單擊文本區域,選擇“更改變量名稱”,然後將其命名為 "text"。這是您從代碼中訪問該組 件時所用的名稱。將按鈕文本設置為 "Filter!"。
雙擊該按鈕,在源代碼編輯器中自動創建一個事件處理方法。在單擊按鈕時,將會調用此方法。將此 方法的主體更改為以下代碼。
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) { String s = text.getText(); s = s.toUpperCase(); text.setText(s); }
右鍵單擊該應用程序,然後選擇“運行”。此操作將啟動新的 NetBeans 平台應用程序並安裝模塊。 新應用程序應如下所示:
選擇“窗口”| "Text"(文本),在文本區域中輸入一些文本,然後單擊 "Filter!"。此時,您應看 到文本將以大寫字母顯示:
您已經學習了如何創建新的 NetBeans 平台應用程序以及如何向其中添加新模塊。
使用 Lookup 的模塊化應用程序
使用 Lookup 的模塊化應用程序
供給前一部分所定義的模塊。
在“項目”窗口中展開新應用程序,右鍵單擊“模塊”節點,然後選擇“添加新模塊”。將新模塊命 名為 "TextFilter",並指定 "org.demo.textfilter" 作為其代碼名稱基,然後按照前一部分中的操作完 成向導步驟,該向導會將模塊添加到先前創建的應用程序中。
右鍵單擊 "TextFilter" 模塊,然後選擇“新建”|“Java 接口”。將該 Java 接口命名為 "TextFilter",在“包”中選擇 "org.demo.textfilter",然後使用編輯器對其進行如下定義:
package org.demo.textfilter; public interface TextFilter { String process(String s); }
右鍵單擊 "TextFilter" 模塊,選擇“屬性”,然後使用“API 版本控制”標簽指定包含接口的包應 當可以在整個應用程序中使用。
創建應用程序的第三個模塊,將其命名為 "MyFilter",並指定 "org.demo.myfilter" 作為其代碼名 稱基。
在新創建的 "MyFilter" 模塊的“項目屬性”對話框中,添加對 "TextFilter" 模塊的依賴關系:
由於上面定義的依賴關系,您現在可以實現在第二個模塊中定義的接口:
package org.demo.myfilter; import org.demo.textfilter.TextFilter; @ServiceProvider(service=TextFilter.class) public class UpperCaseFilter implements TextFilter { public String process(String s) { return s.toUpperCase(); } }
在編譯時,@ServiceProvider 標注將創建 META-INF/services 文件夾並包含一個文件,該文 件按照 JDK 6 ServiceLoader 機制注冊 TextFilter 接口實現。您需要設置對實用程序 API 模塊的依賴 關系,該模塊提供 ServiceProvider 標注。
現在需要更改處理過濾按鈕單擊操作的代碼,以便查找並裝入接口 "TextFilter" 的實現程序。在找 到此實現程序後,將對其調用以過濾文本。
我們需要先在 "WordEngine" 模塊的“項目屬性”對話框中添加對 "TextFilter" 模塊的依賴關系, 然後才能執行此操作。
現在,您可以裝入 "TextFilter" 類的實現,如下所示:
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) { String s = text.getText(); TextFilter filter = Lookup.getDefault().lookup(TextFilter.class); if (filter != null) { s = filter.process(s); } text.setText(s); }
以上代碼操作可通過 JDK 6 "ServiceLoader" 類完成,只是 "Lookup" 類可以用在 JDK 6 以 前的 JDK 中。此外,"Lookup" 類還有許多附加功能,我們將在下一部分進行說明。
現在,您可以運行代碼,並檢查是否和以前一樣可以正常運行。雖然功能相同,但新的模塊化設計 將圖形用戶界面和過濾器實現進行了更明確的劃分。新應用程序還可以非常輕松地實現擴展,只需向應用 程序的類路徑中添加新服務提供程序即可。
作為練習,您可以更改代碼,以便對文本連續應用找到的所有文本過濾器(使用方法 "lookupAll") 。例如,添加一個刪除所有空格的文本過濾器實現,然後測試最終的應用程序。
LookupListener 和 InstanceContent
我們將創建第
LookupListener 和 InstanceContent
"TextTopComponent" 的構造函數,如下所示:
private InstanceContent content; private TextTopComponent() { initComponents(); setName(NbBundle.getMessage(TextTopComponent.class, "CTL_TextTopComponent")); setToolTipText(NbBundle.getMessage(TextTopComponent.class, "HINT_TextTopComponent")); // setIcon(Utilities.loadImage(ICON_PATH, true)); content = new InstanceContent(); associateLookup(new AbstractLookup(content)); }
更改過濾按鈕的代碼,以便在單擊該按鈕時,舊值將被添加到 InstanceContent 對象中。
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) { String s = text.getText(); TextFilter filter = Lookup.getDefault().lookup(TextFilter.class); if (filter != null) { content.add(s); s = filter.process(s); } text.setText(s); }
創建一個名為 "History" 的新模塊,並指定 "com.demo.history" 作為其代碼名稱基。
在 "History" 模塊的 "com.demo.history" 包中,創建一個前綴為 "History" 的新窗口組件。指定 此組件應該顯示在 "editor" 位置。在創建該窗口後,向其中添加一個文本區域。將該文本區域的變量名 稱更改為 "historyText"。
向 HistoryTopComponent 類的構造函數中添加一些代碼,使其偵聽當前活動窗口 String 類的 lookup 事件。該代碼會在文本區域中顯示檢索到的所有 String 對象:
private Lookup.Result result; private HistoryTopComponent() { ... result = org.openide.util.Utilities.actionsGlobalContext().lookupResult (String.class); result.addLookupListener(new LookupListener() { public void resultChanged(LookupEvent e) { historyText.setText(result.allInstances().toString()); } }); }
然後,您可以啟動應用程序並進行試用。結果應與以下屏幕快照中的所示類似:
作為練習,您可以將查找結果的類型由 String 更改為 Object,然後查看 當選擇不同的窗口時會發生什麼情況。
恭喜!至此,您已經使用非常少的編碼工作創建了一個模塊化應用程序的小示例:
此應用程序包含 4 個模塊。當滿足下列條件時,一個模塊中的代碼才能被另一個模塊使用:(1) 第一 個模塊明確公開了包,並且 (2) 第二個模塊設置了對第一個模塊的依賴關系。這樣,NetBeans 平台就可 以幫助您以嚴格的模塊化體系結構組織代碼,從而確保僅在提供代碼的模塊之間設置了約定時才可重用代 碼,否則不能隨意重用。
此外,還引入了 Lookup 類作為模塊間通信的一種機制,該類是 JDK 6 ServiceLoader 方 法的擴展。實現是通過其接口裝入的。無需使用實現的任何代碼,"WordEngine" 模塊便能顯示實現程序 所提供的服務。NetBeans 平台應用程序就是以此方式提供松散耦合的。
要繼續學習模塊化和 NetBeans 平台相關知識,請參見包含 4 個部分的“NetBeans 平台選擇管理” 系列(從此處開始)。之後,可參閱 NetBeans 平台學習資源,您可以在其中選擇與您的特定業務方案最 相關的教程。此外,如果您有與 NetBeans 平台相關的任何類型的問題,可隨時寫信至郵件列表 [email protected],其相關歸檔位於此處。
祝您使用 NetBeans 平台愉快,並期待著您的來信!