概述
Eclipse中最出彩的部分莫過于它的Plugin Framework,可以說Eclipse在一定程度上使得Plugin機(jī)制得以流行,當(dāng)然,Eclipse的優(yōu)勢(shì)不僅僅在此,但正因?yàn)椴捎昧薖lugin機(jī)制,Eclipse才得以被不斷的擴(kuò)充,越來越強(qiáng)大。一直以來就想分析Eclipse的Plugin Framework,由于各種原因一直耽擱,剛好這個(gè)周末沒什么事,下定決心對(duì)其進(jìn)行了研究和分析,方法很原始,就是對(duì)Eclipse的啟動(dòng)過程進(jìn)行分析,基于的是Eclipse 3.1的版本,分析過程就不在這說了,主要是說說分析出來的心得。
架構(gòu)上來講Eclipse基本采用的是Kernel+Core Plugins+Custom Plugins的結(jié)構(gòu)體系,除了Kernel部分外均為Plugin,所以可稱為all are plugins,凡是Plugin的部分都是可被替換的。
OSGI
Eclipse 3.0后采用的是OSGI來作為其Plugin Architecture實(shí)現(xiàn)的依據(jù),鑒于此就得簡單提提OSGI了,主要從Plugin的角度來分析OSGI,OSGI概念中主要分為了Bundle和Service,可以認(rèn)為Bundle是一個(gè)模塊的管理器,主要是通過BundleActivator管理模塊的生命周期,而Service則是這個(gè)模塊可暴露對(duì)外的服務(wù)對(duì)象,這里體現(xiàn)了OSGI和傳統(tǒng)的Plugin Framework不同的一個(gè)地方,管理和靜態(tài)結(jié)構(gòu)分開,在OSGI中通過在manifest.mf文件中增加一些內(nèi)容來發(fā)布Bundle,在其中描述了Bundle的提供商、版本、唯一ID、classpath、暴露對(duì)外的包、所依賴的包;每個(gè)Bundle擁有自己的ClassLoader以及context,通過context可進(jìn)行服務(wù)的注冊(cè)、卸載等,這些操作都會(huì)通過事件機(jī)制廣播給相應(yīng)的其他的Bundle;一般來說都為通過在Bundle中編寫初始需要注冊(cè)的服務(wù)的方法來完成Bundle可供外部使用的服務(wù)的暴露功能;如需要調(diào)用其他Plugin提供的服務(wù)可通過context的getServiceReference先獲取Service的句柄,再通過context.getService(ServiceReference)的方法獲取Service的實(shí)體。
Eclipse Plugin定義
Eclipse中的Plugin的概念為包含一系列服務(wù)的模塊即為一個(gè)Plugin。既然是遵循OSGI的,也就意味著Plugin通常是由Bundle和N多Service共同構(gòu)成的,在此基礎(chǔ)上Eclipse認(rèn)為Plugin之間通常存在兩種關(guān)系,一種為依賴,一種為擴(kuò)展,對(duì)于依賴可通過OSGI中元描述信息里添加需要引用的Plugin即可實(shí)現(xiàn),但擴(kuò)展在OSGI中是沒有定義的,Eclipse采用了一個(gè)Extension Point的方式來實(shí)現(xiàn)Plugin的擴(kuò)展功能。
結(jié)合OSGI
Eclipse遵循OSGI對(duì)于Plugin的ID、版本、提供商、classpath、所依賴的plugin以及可暴露對(duì)外的包均在manifest.mf文件中定義。
Plugin Extension Point
對(duì)于擴(kuò)展,Eclipse采用Extension Point的方式來實(shí)現(xiàn),每個(gè)Plugin可定義自己的Extension Point,同時(shí)也可實(shí)現(xiàn)其他Plugin的Extension Point,由于這個(gè)在OSGI中是未定義的,在Eclipse中仍然通過在plugin.xml中進(jìn)行描述,描述的方法為通過<extension-point id="" name="" schema="">的形式來定義Plugin的擴(kuò)展點(diǎn),通過<extension point="">的形式來定義實(shí)現(xiàn)的其他Plugin的擴(kuò)展點(diǎn),所提供的擴(kuò)展點(diǎn)通過schema的方式進(jìn)行描述,詳細(xì)見eclipse extension-point schema規(guī)范,為了更好的說明擴(kuò)展點(diǎn)這個(gè)概念,舉例如下,如工具欄就是工具欄Plugin提供的一個(gè)擴(kuò)展點(diǎn),其他的Plugin可通過此擴(kuò)展點(diǎn)添加按鈕至工具欄中,并可相應(yīng)的添加按鈕所對(duì)應(yīng)的事件(當(dāng)然,此事件必須實(shí)現(xiàn)工具欄Plugin此擴(kuò)展點(diǎn)所要求的接口),工具欄的Plugin將通過callback的方式來相應(yīng)的響應(yīng)按鈕的動(dòng)作。可見通過Extension Point的方式可以很好的提供Plugin的擴(kuò)展方式以及實(shí)現(xiàn)擴(kuò)展的方式。
Eclipse Plugin Framework
那么Eclipse是如何做到Plugin機(jī)制的實(shí)現(xiàn)的呢??還是先講講Eclipse的設(shè)計(jì)風(fēng)格,Eclipse在設(shè)計(jì)時(shí)有個(gè)重要的分層法則,即語言層相關(guān)和語言層無關(guān)的代碼分開(如jdt.core和core),核心與UI分開(如workbench.ui和workbench.core)這兩個(gè)分層法則,這個(gè)在Eclipse代碼中處處可見,在Plugin Framework部分也充分得體現(xiàn)了這個(gè),遵循OSGI,Eclipse首先是實(shí)現(xiàn)了一個(gè)OSGI Impl,這個(gè)主要通過它的FrameWork、BundleHost、ServiceRegistry、BundleContextImpl等對(duì)象來實(shí)現(xiàn),如果關(guān)心的話大家可以看看這部分的代碼,實(shí)現(xiàn)了Bundle的安裝、觸發(fā)、卸載以及Service的注冊(cè)、卸載、調(diào)用,在Plugin機(jī)制上Eclipse采用的為lazy load的方式,即在調(diào)用時(shí)才進(jìn)行實(shí)際的啟動(dòng),采用的為句柄/實(shí)體的方式來實(shí)現(xiàn),外部則通過OSGI進(jìn)行啟動(dòng)、停止等動(dòng)作,各Plugin則通過BundleContext來進(jìn)行服務(wù)的注冊(cè)、卸載和調(diào)用,這是OSGI的部分實(shí)現(xiàn)的簡單介紹。
那么Extension Point方面Eclipse是如何實(shí)現(xiàn)的呢,在加載Plugin時(shí),Eclipse通過對(duì)plugin.xml的解析獲取其中的<extension-point>節(jié)點(diǎn)和<extension>節(jié)點(diǎn),并相應(yīng)的注冊(cè)到ExtensionRegistry中,而各個(gè)提供擴(kuò)展點(diǎn)的Plugin在提供擴(kuò)展點(diǎn)的地方進(jìn)行處理,如工具欄Plugin提供了工具欄的擴(kuò)展點(diǎn),那么在構(gòu)成工具欄時(shí)Plugin將通過Platform.getPluginRegistry().getExtensionPoint(擴(kuò)展點(diǎn)ID)的方法獲取所有實(shí)現(xiàn)此擴(kuò)展點(diǎn)的集合IExtensionPoint[],通過此集合可獲取IConfigurationElement[],而通過這個(gè)就可以獲取<extension point="">其中的配置,同時(shí)還可通過IConfigurationElement創(chuàng)建回調(diào)對(duì)象的實(shí)例,通過這樣的方法Eclipse也就實(shí)現(xiàn)了對(duì)于Plugin的擴(kuò)展以及擴(kuò)展的功能的回調(diào)。在Plugin Framework中還涉及很多事件機(jī)制的使用,比如Framework的事件機(jī)制,以便在Bundle注冊(cè)、Service注冊(cè)的時(shí)候進(jìn)行通知。
總結(jié)
通過對(duì)Eclipse啟動(dòng)過程的分析,可清晰的看到Eclipse Kernel+Core Plugins+Application Plugins的方式,在代碼中分別對(duì)應(yīng)為loadBasicBundles和registerApplicationServices,loadBasicBundles通過加載config.ini中的osgi.bundles完成基本的bundles的加載,去看看這個(gè)配置會(huì)發(fā)現(xiàn)是org.eclipse.core.runtime還有一個(gè)update,core.runtime又會(huì)通過IDEApplication來完成整個(gè)Eclipse的啟動(dòng),同時(shí)會(huì)注冊(cè)所有與workbench相關(guān)的plugin。
Eclipse由于以前版本的Plugin Framework是沒有采用OSGI的,所以通過EclipseAdaptor的方式來實(shí)現(xiàn)與以往的兼容,目前新的Plugin采用的方式基本就是manifest.mf描述Plugin OSGI部分的信息,Plugin.xml描述擴(kuò)展點(diǎn)的信息。
Eclipse中有非常多優(yōu)秀的設(shè)計(jì),這個(gè)在看它的代碼時(shí)會(huì)有很深的感觸,比如Contributing to Eclipse中提到的Extension Object/Interface的設(shè)計(jì),確實(shí)是非常的不錯(cuò),雖然看到你可能覺得很簡單,關(guān)鍵是要想得到并合適的去使用。
總結(jié)陳詞,^_^,Eclipse Plugin Framework是采用OSGI Impl+Plugin Extension-Point的方式來共同實(shí)現(xiàn)的,實(shí)現(xiàn)了Plugin的部署、編寫、獨(dú)立的Classloader和Context、Plugin中Service的注冊(cè)、Plugin中Service的調(diào)用、Plugin的依賴、Plugin的擴(kuò)展、Plugin生命周期的管理。
帶來的思考
Eclipse Plugin Framework采用的是OSGI的實(shí)現(xiàn),一定程度上我們也能看到OSGI的優(yōu)點(diǎn),那么JMX+IoC方式的Plugin Framework與其的比較又是在哪些方面呢?Eclipse Plugin Framework不足的地方又在哪里呢?哪些地方值得改進(jìn)呢?