1、擴充點(extensionpoint )
擴充點是系統中可以被再次擴充的類或者接口,通過擴充點的定義,可以使得系統的執行過程變得可插入,可任意變化。
2、擴充 ( extension )
擴充式插件内部的一個屬性,一個擴充是針對某個擴充點的一個實作,每個擴充都可以有自己的額外屬性,用于在同一個擴充點實作之間進行區分。擴充必須在插件内部進行定義。
3、插件 ( plugin )
插件實際就是一個虛拟的容器,包含了多個擴充 extension、依賴插件 requireplugins 和自身釋出的庫runtime,插件可以被啟動或者停止。
nutch 為了擴充,預留了很多擴充點 extenstionpoint,同時提供了這些擴充點的基本實作 extension,plugin
用來組織這些擴充,這些都通過配置檔案進行控制,主要的配置檔案包括了多個定義擴充點和插件(擴充)的配置檔案,一個控制加載哪些插件的配置檔案。體系結構圖如下:
1. runtime 屬性描述了其需要的 jar 包,和釋出的 jar 包
2. requires 屬性描述了依賴的插件
3. extension-point 描述了本插件宣布可擴充的擴充點
4. extension 屬性則描述了擴充點的實作
類包括:
1. pluginrepository 是一個通過加載 iconfiguration 配置資訊初始化的插件庫,裡面維護了系統中所有的擴充
點 extensionpoint 和所有的插件 plugin 執行個體
2. extensionpoint 是一個擴充點,通過擴充點的定義,插件 plugin 才能定義實際的擴充 extension,進而實作
擴充,每個 extensionpoint 類執行個體都維護了宣布實作了此擴充點的擴充 extension.
3. plugin 是一個虛拟的組織,提供了一個啟動 start 和一個 shutdown 方法,進而實作了插件的啟動和停止,
他還有一個描述對象 plugindescriptor,負責儲存此插件相關的配置資訊,另外還有一個 pluginclassloader 負責此插件相關類和庫的加載。
通過序列圖可以發現,nutch 加載插件的過程需要 actor 全程直接調用每個關聯對象,最終得到的是插件的實作對象。詳細過程如下:
1. 首先通過 pluginrepository.getconf() 方法加載配置資訊,配置的内容包括插件的目錄,插件的配置檔案資訊 plugin.properties 等,此時 pluginrepository 将根據配置資訊加載各個插件的 plugin.xml,同時根據 plugin.xml 加載插件的依賴類。
2. 當 actor 需要加載某個擴充點的插件的時候,他可以:
1. 首先根據擴充點的名稱,通過 pluginrepository 得到擴充點的執行個體,即 extensionpoint 類的執行個體;
2. 然後調用 extensionpoint 對象的 getextensions 方法,傳回的是實作此擴充點的執行個體清單
(extension[]);
3. 對每個實作的擴充執行個體 extension,調用它的 getextensioninstance() 方法,以得到實際的實作類實
例,此處為 object;
4. 根據實際情況,将 object 轉型為實際的類對象類型,然後調用它們的實作方法,例如 helloworld 方法。
實際整個系統如果使用了插件架構,則插件類的加載是由 pluginclassloader 類完成的,每個 plugin 都有自己的 classloader,此 classloader 繼承自 urlclassloader,并沒有做任何事情:
這個 classloader 是屬于這個插件的,它隻負責加載本插件相關的類、本地庫和依賴插件的釋出 (exported) 庫,也包括一些基本的配置檔案例如 .properties 檔案。
此類的執行個體化過程:
* 首先判斷緩存是否存在
* 加載需要的 jar 包、自身需要的 jar 包,依賴插件釋出的 jar 包
* 加載本地的 properties 檔案
* 構造此 classloader,父 classloader 為 plugindescriptor 的加載者,通常是 contextclassloader
nutch 是一個非常出色的開源搜尋架構,它的插件架構更加是它的一個技術亮點,通過此架構,可以保證 nutch友善的被靈活的擴充而不用修改原來的代碼,通過配置檔案可以簡單友善的控制加載或者不加載哪些插件,而且這些都不需要額外的容器支援。這些都是我們在系統架構設計的時候可以學習和參考的有益經驗。