Eclipse最有魅力的地方就是它的外掛程式體繫結構。在這個體系中重要的概念是擴充點(extension points),也就是為外掛程式提供的介面。每一個外掛程式都是在現有的擴充點上開發,並可能還留有自己的擴充點,以便在這個外掛程式上繼續開發。
由於有了外掛程式,Eclipse系統的核心部分在啟動的時候要完成的工作十分簡單:啟動平台的基礎部分和尋找系統的外掛程式。在Eclipse中實現的絕大部分功能是由相應的外掛程式完成的,比如WrokBench UI外掛程式完成介面的外觀顯示,Resource Management外掛程式完成維護或產生項目或檔案等資源管理工作(在下面的第二個例子就會用到這個外掛程式),而Version and Configuration Management (VCM)外掛程式則負責完成版本控制功能,等等。雖然以上提到的每一個功能都是絕大多數IDE環境所必備的功能,Eclipse卻也把它們都做成了外掛程式模式,甚至用來開發Java程式的開發環境(Java development tooling,JDT)也只不過是Eclipse系統中的一個普通外掛程式而已。整個Eclipse體繫結構就象一個大拼圖,可以不斷的向上加外掛程式,同時,現有外掛程式上還可以再加外掛程式。下面的外掛程式開發樣本就是在WorkBench UI外掛程式中的觀察視窗擴充點上開發的。
本文第一部分介紹過Eclipse的開發介面其中之一就是觀察視窗,它通常配合編輯視窗顯示一些有用的資訊,在這裡我們只簡單產生一個顯示歡迎資訊的觀察視窗,假設新外掛程式的名子叫Welcome。
第一步,先用嚮導建立一個Java項目。我們可以在功能表列選擇FileàNew,或用工具列的嚮導按鍵,或是在資源視窗用滑鼠右鍵菜單中的New,開啟嚮導對話方塊,然後用預設方式建立項目。並在項目中建立一個Welcome.java檔案,代碼如下:
package com.nidapeng.eclipse.plugin;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.SWT;
import org.eclipse.ui.part.ViewPart;
public class Welcome extends ViewPart {
Label label;
public Welcome() {
}
public void createPartControl(Composite parent) {
label = new Label(parent, SWT.WRAP);
label.setText("Welcome to Eclipse");
}
public void setFocus() {
}
}
為使這個程式能正常編譯,要配置它的編譯環境,即指定所需的CLASSPATH。在Eclipse中可以用幾種方法,常用的是兩種:第一是在資源視窗或Java包視窗選中該項目,點擊滑鼠右鍵,在開啟的菜單中選擇屬性(Properties),之後在屬性對話方塊中選擇Java Build PathàLibraries,用Add External JARs功能添加三個包,它們都是Eclipse的現有外掛程式的類包,可以在"你的Eclipse安裝路徑/plugins"下面的相應路徑中找到。分別是org.eclipse.core.runtime外掛程式中的runtime.jar,org.eclipse.swt中的swt.jar和org.eclipse.ui中的workbench.jar。第二種指定CLASSPATH的方法是先將以上提到的三個包直接匯入到Eclipse中的某下一個項目中。如果匯入到和Welcome.java相同的項目中,則無需進一步指定CLASSPATH,否則需要在項目的屬性菜單中選擇Java Build PathàProjects,然後選中這三個包所在的項目。
在我們的項目中還要產生一個XML檔案,它的名字必須plugin.xml。代碼如下:
<?xml version="1.0" encoding="UTF-8"?>
<plugin
id="com.nidapeng.eclipse.plugin"
name="Welcome to Eclipse"
version="1.0"
provider-name="Ni Dapeng">
<requires>
<import plugin="org.eclipse.ui"/>
</requires>
<runtime>
<library name="welcome.jar"/>
</runtime>
<extension
point="org.eclipse.ui.views">
<category
name="Welcome"
id="com.nidapeng.eclipse.plugin.category1">
</category>
<view
name="Welcome to Eclipse"
category="com.nidapeng.eclipse.plugin.category1"
class="com.nidapeng.eclipse.plugin.Welcome"
id="com.nidapeng.eclipse.plugin.view1">
</view>
</extension>
</plugin>
在plugin.xml中一共有四個主要的標籤:plugin,requires,runtime,extension。其中plugin標籤的屬性提供的是我們要開發的Welcome外掛程式的基本資料,除了name,version,provider-name等,最重要的是id,它要求不能和現有的Eclipse外掛程式id有衝突,因此我們用包名作為外掛程式的id。requires標籤中所列出的是需要的外掛程式,這裡我們要用到Eclipse Workbench和SWT API,因此匯入了org.eclipse.ui外掛程式。runtime標籤指明的是我們開發的外掛程式所在JAR包的檔案名稱。extension標籤是外掛程式擴充點的資訊。org.eclipse.ui.views是Eclipse系統提供的觀察視窗擴充點,我們的例子是一個觀察視窗(View),這表明我們是要在 org.eclipse.ui.views擴充點上進一步開發。extension中還包括category和view兩個標籤,在後續的啟動Welcome外掛程式步驟中,我們就會知道這兩個標籤的含義。要注意的是category和view標籤的id的唯一性,並且在view的屬性中聲明了Welcome外掛程式的類名。
在Eclipse中為plugin.xml提供了預設可視化的編輯器,在編寫plugin.xml過程中可以藉助這個編輯器完成一些工作。如果你直接錄入了plugin.xml檔案原始碼,還可以用這個編輯器校正你的代碼:如果編輯器不能正確讀入,就表明你的plugin.xml有一些問題。
在確認Weclome.java和plugin.xml都正確無誤之後,可以用Eclipse功能表列中的Export命令將Weclome.java匯出為JAR檔案,它的名子應該和plugin.xml中runtime聲明的JAR相一致。同時匯出plugin.xml。安裝Welcome外掛程式的方法和本文第一部分介紹的安裝Tomcat外掛程式方法是一樣的:首先在"Eclipse的安裝路徑/plugins"路徑下面建立一個com.nidapeng.eclipse.plugin路徑,然後將Weclome.jar和plugin.xml拷到這個路徑下。之後必需重新啟動Eclipse,在Eclipse啟動的時候,它會搜尋所有在外掛程式路徑下的外掛程式並註冊它們(僅僅是註冊,只有在需要某個外掛程式的時候,Eclipse才會啟動它)。在重新啟動的Eclipse的功能表列中選擇PerspectiveàShow ViewàOthers,在開啟的對話方塊中我們會找到在plugin.xml中extension的category標籤中聲明的name屬性:Welcome。在Welcome的支結點中包含了view標籤name屬性:Welcome to Eclipse。選中它並確認,Welcome視窗就會顯示在Eclipse Workbench上的某個位置 。如果在執行了以上操作,但沒有顯示新視窗,可以再次開啟Show View菜單,此時在菜單中應該有新一頂選擇:Welcome to Eclipse,然後選中它。
上面我們完成了一個觀察視窗的外掛程式,但這個操作過程對開發稍微複雜一些的外掛程式就顯得不太方便了:每次測試都要將代碼打包,發布,再重新啟動Eclipse系統!為此Eclipse提供了一個專門為開發外掛程式而做外掛程式(有點繞嘴):Plug-in Development Environment(PDE)。本文前面曾提到,目前Eclipse的Release或Stable版本預設提供了這個外掛程式,因此如果安裝的Eclipse是這兩個版本中的一個就可以直接進行下面的步驟。下面我們再用PDE環境開發一個稍微複雜一些的外掛程式。
第一步仍然要建立一個項目,只是在嚮導中不是用Java項目,而是Plug-in Development中的Plug-in Project。在應用嚮導產生新項目的時候,要注意兩點:第一是PDE的項目名稱就是plugin的id,因此要保證它的唯一性,這裡我們的項目名是com.nidapeng.eclipse.plugin.pde。其次為了進一步說明Eclipse外掛程式的結構,在Plug-in Code Generators中,選擇用嚮導模板產生一個預設的外掛程式,六:
圖六
這個用預設方式產生的外掛程式類對於我們將要的代碼並不是必需的,也可以用產生空外掛程式的方式建立我們的項目,這樣做只是為進一步說明Eclipse的外掛程式結構。
項目產生之後,在我們的項目中會包含一個PdePlugin.java檔案,它就是以預設方式產生的外掛程式類。注意到它繼承了AbstractUIPlugin類,而AbstractUIPlugin類實現了org.eclipse.ui.plugin介面。事實上,所有的Eclipse外掛程式都會有一個相應的實現plugin介面的類,這個類將是新外掛程式的主類(類似於有main()函數的Java類),它負責管理外掛程式的生存期。在我們的AbstractUIPlugin繼承子類中,可以用singleton模式來儲存在Eclipse中的產生的該外掛程式的第一個也是唯一執行個體,一般來說,在該繼承子類中也要實現一個getDefault()方法以返回當前外掛程式的執行個體。而且,當Eclipse首次使用該外掛程式的時候,這個主類將是第一個被調用的類,因此我們也可以在它的代碼中執行一些初始化的工作。而且如果外掛程式需要使用Preferences,Dialogs或Images資源,也可以通過這個類中的相應方法來得到它們的執行個體,如用其中的getDialogSettings(),getPreferenceStore(),getImageRegistry()方法。
但是象前面提到的,PdePlugin.java對下面的例子並不是必需的,我們不用對它進行任何修改。在我們第一個例子中的Weclome外掛程式,根本就沒有產生AbstractUIPlugin的繼承子類,此時系統會自動為Weclome外掛程式產生一個預設的主類(類似於Java類建構函式,如果沒有聲明,系統會指定一個預設的建構函式)。
下面的代碼是才真正實現了我們新外掛程式的功能,假設這個外掛程式名子是NoticeView:
package com.nidapeng.eclipse.plugin.pde;
import org.eclipse.core.resources.*;
import org.eclipse.core.resources.IResourceChangeEvent;
import org.eclipse.core.runtime.CoreException;
import java.util.ResourceBundle;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.part.ViewPart;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Display;
public class NoticeView extends ViewPart implements
Runnable,IResourceChangeListener ,IResourceDeltaVisitor{
private ResourceBundle resourceBundle;
private Label label;
private Display disp;
private String dispTxt;
public NoticeView() {
ResourcesPlugin.getWorkspace().addResourceChangeListener(this,
IResourceChangeEvent.PRE_CLOSE
| IResourceChangeEvent.PRE_DELETE
| IResourceChangeEvent.PRE_AUTO_BUILD
| IResourceChangeEvent.POST_AUTO_BUILD
| IResourceChangeEvent.POST_CHANGE);
}
public static IWorkspace getWorkspace() {
//ResourcesPlugin外掛程式的主類!
return ResourcesPlugin.getWorkspace();
}
public void createPartControl(Composite parent) {
label = new Label(parent, SWT.WRAP);
label.setText("change your project status...");
disp = Display.getDefault();
}
public void setFocus() {
}
// 實現IResourceChangeListener介面中的resourceChanged方法
public void resourceChanged(IResourceChangeEvent event) {
IResource res = event.getResource();
switch (event.getType()) {
case IResourceChangeEvent.PRE_CLOSE :
dispTxt = res.getFullPath() + " is about to closed!";
break;
case IResourceChangeEvent.PRE_DELETE :
dispTxt = res.getFullPath() + " is about to be deleted!";
break;
case IResourceChangeEvent.POST_CHANGE :
try {
event.getDelta().accept(this);
} catch (CoreException e) {
e.printStackTrace();
}
break;
case IResourceChangeEvent.PRE_AUTO_BUILD :
try {
event.getDelta().accept(this);
} catch (CoreException e) {
e.printStackTrace();
}
break;
case IResourceChangeEvent.POST_AUTO_BUILD :
try {
event.getDelta().accept(this);
} catch (CoreException e) {
e.printStackTrace();
}
break;
}
disp.syncExec(this);
}
// 實現IResourceDeltaVisitor介面中的visit方法
public boolean visit(IResourceDelta delta) {
IResource res = delta.getResource();
switch (delta.getKind()) {
case IResourceDelta.ADDED :
dispTxt = "Resource "+res.getFullPath()+" was added.";
break;
case IResourceDelta.REMOVED:
dispTxt = "Resource "+res.getFullPath()+" was removed.";
break;
case IResourceDelta.CHANGED :
dispTxt = "Resource "+res.getFullPath()+" has changed.";
break;
}
return true; // visit the children
}
// 實現Runnable介面中的run方法
public void run() {
try {
label.setText(dispTxt);
} catch (Exception e) {
e.printStackTrace();
}
}
}
象上面的第一個Welcome外掛程式,這個新外掛程式同樣繼承了ViewPart,不同的是實現了三個介面:Runnable,IResourceChangeListener ,IResourceDeltaVisitor。其中的Runnable大家應該很熟悉:多線程的介面。而IResourceChangeListener和IResourceDeltaVisitor是Eclipse系統中的資源介面,這裡的資源是指Eclipse中的項目或檔案等。在下面運行NoticeView外掛程式的過程中你可以通過添加、開啟、刪除項目或檔案來觸發這兩個介面中的事件,並在我們的觀察視窗中顯示相關資訊。
在程式中比較奇怪部分的是在resourceChanged()函數裡面,並沒有象大家想象的那樣直接調用label.setText()方法來顯示資訊,而是調用了disp.syncExec(this),其中的disp是Display類型的對象。這是因為resourceChanged()方法啟動並執行線程和lable所在外掛程式啟動並執行Eclipse主線程並不是同一個線程,如果直接調用label.setText()方法,會拋出一個異常。
下面還需要對項目中的plugin.xml進行一些改動,主要就是加上擴充點聲明:
<?xml version="1.0" encoding="UTF-8"?>
<plugin
id="com.nidapeng.eclipse.plugin.pde"
name="Pde Plugin"
version="1.0.0"
provider-name="NIDAPENG"
class="com.nidapeng.eclipse.plugin.pde.PdePlugin">
<requires>
<import plugin="org.eclipse.core.runtime"/>
<import plugin="org.eclipse.core.resources"/>
<import plugin="org.eclipse.ui"/>
</requires>
<runtime>
<library name="pde.jar"/>
</runtime>
<extension
id="NoticeView"
name="Notice View"
point="org.eclipse.ui.views">
<category
name="Notice"
id="com.nidapeng.eclipse.plugin.pde.category1">
</category>
<view
name="Notice Resource View"
category="com.nidapeng.eclipse.plugin.pde.category1"
class="com.nidapeng.eclipse.plugin.pde.NoticeView"
id="com.nidapeng.eclipse.plugin.pde.view1">
</view>
</extension>
</plugin>
這個xml檔案和Welcome外掛程式的plugin.xml非常接近,這裡就不做過多的說明了。
要運行這個外掛程式,可以直接用Eclipse中的運行按鈕,因為這個項目是一個Plug-in Project,此時項目會自動以Run-time Workbench方式運行。運行後,會產生一個和當前Eclipse完全一致的平台,在這個平台上可以直接運行NoticeView外掛程式,查看這個外掛程式到底會執行什麼功能,也可以用直接Run-time Workbench方式調試外掛程式。這裡省去了安裝外掛程式,重啟動Eclipse等過程,可以看到用PDE開發外掛程式的過程比直接用Java開發環境簡潔了很多!
Eclipse的開發不僅僅限於外掛程式的開發,它還可以取代Java中的標準Swing,進行基於Java的獨立應用程式GUI開發。它帶來的好處是顯而易見的:高速,資源佔用低,跨平台,代碼開放,有大公司的支援等等。
由於Eclipse目前還在開發階段,筆者在用它偵錯工具時發現有些效能還不是十分的穩定,一些地方會遇到奇怪的問題,要求使用者能想一些辦法解決。不過,以現在Eclipse的開發速度,相信過不了多久,它的各種功能會逐步完善。目前Eclipse雖然有種種不足,但瑕不掩玉,筆者對Eclipse的總體印象還是非常不錯的,運行速度,資源佔用都要好於IVJ,操作起來也大多順手,而且即使在現階段也很少有意外退出等重大的Bug發生,希望未來的Eclipse能真正達到IVJ的功能,VisualCafe的速度,成為廣大程式員開發軟體的一大利器!