基于插件的體系結構
Eclipse 的價(jià)值是它為創(chuàng )建可擴展的集成開(kāi)發(fā)環(huán)境提供了一個(gè)開(kāi)放源碼平臺。這個(gè)平臺允許任何人構建與環(huán)境和其它工具無(wú)縫集成的工具。他是一個(gè)成熟的、精心設計的以及可擴展的體系結構。
工具與 Eclipse 無(wú)縫集成的關(guān)鍵是插件。除了小型的運行時(shí)內核之外,Eclipse 中的所有東西都是插件。從這個(gè)角度來(lái)講,所有功能部件都是以同等的方式創(chuàng )建的。從這個(gè)角度來(lái)講,所有功能部件都是以同等的方式創(chuàng )建的。
但是,某些插件比其它插件更重要些。Workbench 和 Workspace 是 Eclipse 平臺的兩個(gè)必備的插件 — 它們提供了大多數插件使用的擴展點(diǎn),如圖 1 所示。插件需要擴展點(diǎn)才可以插入,這樣它才能運行。
圖 1. Eclipse Workbench 和 Workspace:必備的插件支持
Workbench 組件包含了一些擴展點(diǎn),例如,允許您的插件擴展 Eclipse 用戶(hù)界面,使這些用戶(hù)界面帶有菜單選擇和工具欄按鈕;請求不同類(lèi)型事件的通知;以及創(chuàng )建新視圖。Workspace 組件包含了可以讓您與資源(包括項目和文件)交互的擴展點(diǎn)。
當然,其它插件可以擴展的 Eclipse 組件并非只有 Workbench 和 Workspace。此外,還有一個(gè) Debug 組件可以讓您的插件啟動(dòng)程序、與正在運行的程序交互,以及處理錯誤 — 這是構建調試器所必需的。雖然 Debug 組件對于某些類(lèi)型的應用程序是必需的,但大多數應用程序并不需要它。
還有一個(gè) Team 組件允許 Eclipse 資源與版本控制系統(VCS)交互,但除非您正在構建 VCS 的 Eclipse 客戶(hù)機,否則 Team 組件,就象 Debug 組件一樣,不會(huì )擴展或增強它的功能。
最后,還有一個(gè) Help 組件可以讓您提供應用程序的聯(lián)機文檔和與上下文敏感的幫助。沒(méi)有人會(huì )否認幫助文檔是專(zhuān)業(yè)應用程序必備的部分,但它并不是插件功能的必要部分。
上述每個(gè)組件提供的擴展點(diǎn)都記錄在 Eclipse Platform Help 中,該幫助在 Platform Plug-in Developer 指南的參考部分中。乍一看,尤其是 API 參考大全的 Workbench 部分,一開(kāi)始會(huì )令人望而卻步。我們不會(huì )深入了解眾多可用擴展點(diǎn)的詳細信息,而只是粗略地看一個(gè)簡(jiǎn)單插件及其組件。
插件簡(jiǎn)介
創(chuàng )建插件最簡(jiǎn)單的方法是使用 Plug-in Development Environment(PDE)。PDE 和 Java Development Tooling(JDT)IDE 是 Eclipse 的標準擴展。PDE 提供了一些向導以幫助創(chuàng )建插件,包括我們將在這里研究的“Hello, world”示例。
從 Eclipse 菜單,選擇 File=>New=>Other(或按 Ctrl-N),然后選擇 Select 對話(huà)框左邊的 Plug-in Development 向導。在 Select 對話(huà)框的右邊,選擇 Plug-in Project。按 Next。
在下一屏上,輸入項目名稱(chēng);我使用了 com.example.hello。再次按 Next。在下一屏上,請注意,插件標識就與項目名稱(chēng)相同。使用項目名稱(chēng)作為插件標識可以將該插件與另一個(gè)插件的名稱(chēng)發(fā)生沖突的機會(huì )減到最小。再按一次 Next。下一屏讓您選擇是手工創(chuàng )建初始插件代碼,還是運行代碼生成向導。保留代碼生成向導的缺省選項,選擇“Hello, World”,然后按 Next,如圖 2 所示。
圖 2. 選擇“Hello, World”代碼生成向導
下一屏要求一些附加信息。請注意這一屏上的信息:它包含了插件名稱(chēng)、版本號、提供者名稱(chēng)和類(lèi)名。這些是關(guān)于插件的重要信息,我們將在稍后研究??梢越邮芟驅峁┑娜笔≈?。按 Next。在下一屏幕上,接受包名、類(lèi)名和消息文本的缺省值。選擇“Add the action set to the resource perspective”復選框。按 Finish。如果接到通知:向導需要啟用某些其它插件才能完成,那么按 OK。過(guò)一會(huì )兒,向導將完成,而在您的工作區中將會(huì )有一個(gè)新的項目,名為 com.example.hello,如圖 3 所示。
圖 3. PDE 透視圖:Welcome to Hello Plug-in
在 Package Explorer 中,工作臺的左邊是向導創(chuàng )建的一些東西的概述。大多數項都不引人關(guān)注:包括項目類(lèi)路徑中的許多 .jar 文件(這些包括插件和 Java 運行時(shí)所需的 Eclipse 類(lèi))、一個(gè)圖標文件夾(包含了工具欄按鈕的圖形),以及 build.properties 文件(包含自動(dòng)構建腳本所使用的變量)。
這里最有意思的東西是 src 文件夾,它包含了插件和 plugin.xml 文件的源代碼 — plug-in.xml 是插件的清單文件。我們將先查看 plugin.xml。
插件清單文件
插件清單文件 plugin.xml 包含了 Eclipse 將插件集成到框架所使用的描述信息。缺省情況下,當第一次創(chuàng )建插件時(shí),會(huì )在清單編輯器區域中打開(kāi) plugin.xml。編輯器底部的選項卡讓您可以選擇關(guān)于插件的不同信息集合。Welcome 選項卡顯示了消息“Welcome to Hello Plug-In”,并且簡(jiǎn)要討論了所使用的模板和關(guān)于使用 Eclipse 實(shí)現插件的提示。選擇“Source”選項卡可以讓您查看 plugin.xml 文件的完整源代碼。
讓我們看看插件清單文件的各個(gè)部分。首先是關(guān)于插件的常規信息,包括它的名稱(chēng)、版本號、實(shí)現它的類(lèi)文件的名稱(chēng)和 .jar 文件名。
清單 1. 插件清單文件 — 常規信息
<?xmlversion="1.0" encoding="UTF-8"?><plugin id="com.example.hello" name="Hello Plug-in" version="1.0.0" provider-name="EXAMPLE" class="com.example.hello.HelloPlugin"> <runtime> <library name="hello.jar"/> </runtime>
接著(zhù),列出了我們的插件所需的插件:
清單 2. 插件清單文件 — 必需的插件
<requires> <import plugin="org.eclipse.core.resources"/> <import plugin="org.eclipse.ui"/> </requires>
列出的第一個(gè)插件 org.eclipse.core.resources 是工作區插件,但實(shí)際上我們的插件并不需要它。第二個(gè)插件 org.eclipse.ui 是工作臺。我們需要工作臺插件,因為我們將擴展它的兩個(gè)擴展點(diǎn),正如后面的 extension 標記所指出的。
第一個(gè) extension 標記擁有點(diǎn)屬性 org.eclipse.ui.actionSets。操作集合是插件添加到工作臺用戶(hù)界面的一組基值 — 即,菜單、菜單項和工具欄。操作集合分組了基值,這樣用戶(hù)可以更方便地管理它們。例如,我們的 Hello 插件的菜單和工具欄項將出現在 Resource 透視圖中,因為當在運行代碼生成向導時(shí),我們做了這樣的選擇。如果用戶(hù)要更改它,可以使用 Window=>Customize Perspective 菜單選項從要在 Resource 透視圖中顯示的項中除去“Sample Action Set”。
圖 4. 定制 Resource 透視圖
操作集合包含了兩個(gè)標記:menu 標記(描述菜單項應該出現在工作臺菜單的什么位置,以及如何出現)和action 標記(描述它應該做什么)— 尤其是 action 標記標識了執行操作的類(lèi)。注:這個(gè)類(lèi)不是上面列出的插件類(lèi)。
清單 3. 操作集合
<extension point="org.eclipse.ui.actionSets"> <actionSet label="Sample Action Set" visible="true" id="com.example.hello.actionSet"> <menu label="Sample &Menu" id="sampleMenu"> <separator name="sampleGroup"> </separator> </menu> <action label="&Sample Action" icon="icons/sample.gif" class="com.example.hello.actions.SampleAction" tooltip="Hello, Eclipse world" menubarPath="sampleMenu/sampleGroup" toolbarPath="sampleGroup" id="com.example.hello.actions.SampleAction"> </action> </actionSet> </extension>
許多菜單和操作屬性的目的相當明顯 — 例如,提供工具提示文本和標識工具欄項的圖形。但還要注意 action 標記中的 menubarPath:這個(gè)屬性標識了 menu 標記中定義的哪個(gè)菜單項調用 action 標記中定義的操作。有關(guān)這個(gè)和其它工作臺擴展點(diǎn)的詳細信息,請參考 Platform Plug-in Developer Guide,尤其是“Plugging into the workbench”章節(可以從 Eclipse 的幫助菜單中獲取該指南)。
由于我們選擇了將插件添加到 Resource 透視圖,于是生成了第二個(gè) extension 標記。這個(gè)標記會(huì )導致當 Eclipse 第一次啟動(dòng)并裝入我們的插件時(shí),將插件添加到 Resource 透視圖。
清單 4. extension 標記
<extension point="org.eclipse.ui.perspectiveExtensions"> <perspectiveExtension targetID="org.eclipse.ui.resourcePerspective"> <actionSet id="com.example.hello.actionSet"> </actionSet> </perspectiveExtension> </extension></plugin>
如果忽略這最后一個(gè) extension,用戶(hù)就需要使用 Window=>Customize Perspective 將插件添加到 Resource(或其它)透視圖。
插件源代碼代碼生成向導生成了兩個(gè) Java 源文件,打開(kāi) PDE Package Explorer 中的 src 文件夾就可以看到它們。第一個(gè)文件 HelloPlugin.java 是插件類(lèi),它繼承了 AbstractUIPlugin 抽象類(lèi)。HelloPlugin 負責管理插件的生命周期,在更為擴展的應用程序中,它負責維護諸如對話(huà)框設置和用戶(hù)首選項等內容。HelloPlugin 要做的事就這么多:清單 5. HelloPlugin
packagecom.example.hello.actions;import org.eclipse.ui.plugin.*;import org.eclipse.core.runtime.*;import org.eclipse.core.resources.*;import java.util.*;/** * The main plugin class to be used in the desktop. */public class HelloPlugin extends AbstractUIPlugin{ //The shared instance. private static HelloPlugin plugin; //Resource bundle. private ResourceBundle resourceBundle; /** * The constructor. */ public HelloPlugin(IPluginDescriptor descriptor) { super(descriptor); plugin = this; try { resourceBundle= ResourceBundle.getBundle( "com.example.hello.HelloPluginResources"); } catch (MissingResourceException x) { resourceBundle = null; } } /** * Returns the shared instance. */ public static HelloPlugin getDefault() { return plugin; } /** * Returns the workspace instance. */ public static IWorkspace getWorkspace() { return ResourcesPlugin.getWorkspace(); } /** * Returns the string from the plugin‘s resource bundle, * or ‘key‘ if not found. */ public static String getResourceString(String key) { ResourceBundle bundle= HelloPlugin.getDefault().getResourceBundle(); try { return bundle.getString(key); } catch (MissingResourceException e) { return key; } } /** * Returns the plugin‘s resource bundle, */ public ResourceBundle getResourceBundle() { return resourceBundle; }}
第二個(gè)源文件 SampleAction.java 包含的類(lèi)將執行在清單文件的操作集合中指定的操作。SampleAction 實(shí)現了 IWorkbenchWindowActionDelegate 接口,它允許 Eclipse 使用插件的代理,這樣不是在萬(wàn)不得已的情況下,Eclipse 就無(wú)需裝入插件(這項優(yōu)化工作使在裝入插件時(shí)發(fā)生內存和性能方面的問(wèn)題降到最低)。IWorkbenchWindowActionDelegate 接口方法使插件可以與代理進(jìn)行交互:
清單 6. IWorkbenchWindowActionDelegate 接口方法
package com.example.hello.actions;import org.eclipse.jface.action.IAction;import org.eclipse.jface.viewers.ISelection;import org.eclipse.ui.IWorkbenchWindow;import org.eclipse.ui.IWorkbenchWindowActionDelegate;import org.eclipse.jface.dialogs.MessageDialog;/** * Our sample action implements workbench action delegate. * The action proxy will be created by the workbench and * shown in the UI. When the user tries to use the action, * this delegate will be created and execution will be * delegated to it. * @see IWorkbenchWindowActionDelegate */public class SampleAction implements IWorkbenchWindowActionDelegate { private IWorkbenchWindow window; /** * The constructor. */ public SampleAction() { } /** * The action has been activated. The argument of the * method represents the ‘real‘ action sitting * in the workbench UI. * @see IWorkbenchWindowActionDelegate#run */ public void run(IAction action) { MessageDialog.openInformation( window.getShell(), "Hello Plug-in", "Hello, Eclipse world"); } /** * Selection in the workbench has been changed. We * can change the state of the ‘real‘ action here * if we want, but this can only happen after * the delegate has been created. * @see IWorkbenchWindowActionDelegate#selectionChanged */ public void selectionChanged(IAction action, ISelection selection) { } /** * We can use this method to dispose of any system * resources we previously allocated. * @see IWorkbenchWindowActionDelegate#dispose */ public void dispose() { } /** * We will cache window object in order to * be able to provide parent shell for the message dialog. * @see IWorkbenchWindowActionDelegate#init */ public void init(IWorkbenchWindow window) { this.window = window; }}
運行和調試插件
當開(kāi)發(fā) Eclipse 的插件時(shí),必須停止 Eclipse 并用新的插件重新啟動(dòng)它以便進(jìn)行測試和調試,這很笨拙。幸好,Eclipse PDE 提供了一個(gè)自托管(self-hosted)的開(kāi)發(fā)環(huán)境,它讓您無(wú)需將插件安裝在工作臺的單獨實(shí)例中即可運行。
要運行 Hello 插件,選擇 Run=>Run As=>Run-time Workbench 來(lái)啟動(dòng)另一個(gè) Workbench 實(shí)例,而該實(shí)例添加了插件的菜單選項和工具欄,如圖 5 所示。
圖 5. 在運行時(shí)工作臺中運行的 Hello 插件
我們可以通過(guò)單擊工具欄按鈕或從“Sample Menu”菜單激活插件。任何一種方法都會(huì )生成一個(gè)框,其標題是“Hello Plug-in”,內容是“Hello, Eclipse world”,以及一個(gè) OK 按鈕,按該按鈕可以關(guān)閉這個(gè)框。
通過(guò)選擇 Run=>Debug As=>Run-time Workbench,按類(lèi)似的方法調試插件。這次,當插件在第二個(gè)工作臺實(shí)例中運行時(shí),我們可以在最初的工作臺中單步執行源代碼,以及檢查變量等。一旦插件經(jīng)過(guò)測試并準備發(fā)布,我們就需要將它適當打包,以便在 Eclipse 中安裝。
打包插件 Eclipse 在啟動(dòng)時(shí)會(huì )查看其插件目錄來(lái)確定要裝入哪些插件。要安裝插件,我們需要在插件目錄中創(chuàng )建一個(gè)子目錄,并將程序文件和清單文件復制到那里。建議目錄名稱(chēng)能表示插件的標識,并且后面跟下劃線(xiàn)和版本號,但是這種做法不是必需的。假設 Eclipse 安裝在 C:\eclipse 中;我們要創(chuàng )建一個(gè)目錄:
C:\eclipse\plugins\com.example.hello_1.0.0.
按照 Java 程序的標準,我們的程序文件需要歸檔到 .jar 文件中 —
我們的插件清單文件,您也許記得它包含這個(gè)項:
<runtime> <library name="hello.jar"/> </runtime>
要創(chuàng )建 hello.jar 文件,我們可以通過(guò)突出顯示項目名稱(chēng),并從 Eclipse 菜單選擇 File=>Export,以導出插件文件。選擇 JAR 文件作為導出方式,按 Next,然后瀏覽到我們?yōu)樗鼊?chuàng )建的目錄。下一步,我們還需要將 plugin.xml 文件復制到這個(gè)目錄。也可以使用 File=>Export 菜單選項(但請要記住選擇 File System 作為導出目的地)。
這就是安裝插件所需的全部操作,但您將需要停止并重新啟動(dòng) Eclipse,以便能識別這個(gè)新的插件。從幫助菜單中選擇“About Eclipse Platform”,可以找到關(guān)于已安裝插件的信息,包括版本號。在出現的屏幕上有一個(gè)按鈕是 Plug-in Details;向下滾動(dòng)列表來(lái)尋找 Hello 插件及其版本號。
更新插件版本
在目錄名稱(chēng)中包含版本號的目的是允許在同一臺機器上共存某個(gè)插件的多個(gè)版本(每次只裝入一個(gè)版本)。我們可以通過(guò)創(chuàng )建一個(gè) Hello 插件的已更新版本來(lái)看看這是如何工作的:例如,將 plugin.xml 文件中的版本號更改成“1.0.1”,然后將 SampleAction.java 中的文本更改成“New and improved Hello, Eclipse world”。從 Eclipse 菜單中選擇 Project=> Rebuild All。下一步,將項目文件以 JAR 形式導出到新的插件目錄,例如,com.example.hello_1.0.1。將修訂過(guò)的 plugin.xml 文件復制到同一個(gè)目錄中。當停止并重新啟動(dòng) Eclipse 時(shí),只會(huì )裝入已更新的插件。
插件片段和功能部件
Eclipse 由插件組成,但在開(kāi)發(fā) Eclipse 的插件時(shí),還要慎重考慮另外兩個(gè)級別的組件 — 插件片段和功能部件。
插件片段(如名稱(chēng)所暗示的)是完整插件的組成部分 — 目標插件。片段提供的功能與目標插件的功能合并。片段可以用于將插件本地化成各種語(yǔ)言;在無(wú)需形成一個(gè)全新發(fā)行版的情況下,以增量形式將功能部件添加到現有插件,或者提供特定于平臺的功能。在許多方面,片段與插件一樣。主要的區別就是片段沒(méi)有插件類(lèi) — 片段的生命周期由其目標插件管理。此外,片段的清單文件叫作 fragment.xml,它列出了目標插件的標識和版本號,以及片段的標識和版本號。
另一方面,插件功能部件根本不包含編碼。在 Eclipse 體系結構術(shù)語(yǔ)中,功能部件是將一組相關(guān)插件打包到完整的產(chǎn)品中。例如,JDT 是包含了象 Java 編輯器、調試器和控制臺這樣的插件的功能部件。名為 feature.xml 的清單文件描述了一個(gè)功能部件歸檔文件。在其中,該清單文件包含了對該功能部件所包含的插件和其它資源的引用、關(guān)于如何更新該功能部件的信息、版權信息和許可證信息。
在 Eclipse 中,主功能部件設置了 Eclipse 平臺的外觀(guān)。主功能部件旨在確定諸如給予 Eclipse 其身份的閃屏和其它特征之類(lèi)的東西。Eclipse 只允許一個(gè)主功能部件。用這種方式,通過(guò)創(chuàng )建一組插件,將它們打包到功能部件中,并且使這個(gè)功能部件成為主功能部件,就可以重新創(chuàng )建 Eclipse 的品牌,并將它用于創(chuàng )建全新且不同的產(chǎn)品。如果從 Eclipse.org下載,缺省主功能部件是 eclipse.org.platform。