了解 OSGi 概念。 嘗試遵循拼圖方法
已發表: 2013-04-20OSGi
在今天變得非常流行,這要歸功於它的模塊化方法和在模塊之間強制執行邏輯邊界的能力。 當我們第一次發現它時,問題是從哪裡開始了解它是如何工作的?
為了理解 OSGi 概念,我們將嘗試遵循拼圖方法,這個想法是從這項技術的瑣碎部分開始,然後搜索與找到的相關的其他部分。 為了組裝拼圖,我們將得到 JArchitect 的協助,這將有助於檢測 OSGi 內部設計。
更具體地說,我們用 JArchitect 分析使用 OSGi 技術的應用程序,它涉及使用 OSGi 容器 Equinox 的著名 eclipse IDE。
讓我們從典型的 OSGi 定義開始:
OSGi 通過為當今的大型分佈式系統以及小型嵌入式應用程序提供模塊化架構來降低複雜性。 從內部和現成的模塊構建系統顯著降低了複雜性,從而降低了開發和維護費用。 OSGi 編程模型實現了基於組件的系統的承諾。
OSGi 最重要的部分是模塊化,讓我們來看看什麼是 OSGi 模塊?
OSGI 模塊稱為捆綁包,因此每個應用程序都至少包含一個捆綁包。
這些捆綁包在容器內執行,並且作為容器管理模塊的每種模塊化方法,問題是哪個合約必須實現要集成到容器中的每個模塊?
讓我們以包 org.eclipse.equinox.jsp.jasper 為例,搜索其所有實現的接口,為此我們可以執行以下 CQLinq 請求:
1 2 3 4 |
from t in Types where t . ParentProject . Name ==” org . eclipse . equinox . jsp . jasper_1 . 0.300.v20110502 “ let interfaces = t . InterfacesImplemented from i in interfaces select i |
實現了 OSGi 包中的 BundleActivator 接口,該接口包含兩個方法 start 和 stop 用於自定義 bundle 的啟動和停止。
bundle 的另一個特性是它的清單文件,這裡是 org.eclipse.equinox.jsp.jasper 清單文件的一部分:
1 2 3 4 5 6 7 8 9 |
Manifest - Version : 1.0 Bundle - Localization : plugin Bundle - RequiredExecutionEnvironment : CDC - 1.0 / Foundation - 1.0 , J2SE - 1.3 Bundle - SymbolicName : org . eclipse . equinox . jsp . jasper Eclipse - LazyStart : true Eclipse - SourceReferences : scm : cvs : pserver : dev . eclipse . org : / cvsroot / rt : org . eclipse . equinox / server - side / bundles / org . eclipse . equinox . jsp . jaspe r ; tag = v20110502 Bundle - Activator : org . eclipse . equinox . internal . jsp . jasper . Activator |
正如我們所看到的,這個清單包含容器所需的一些元信息,例如指定實現 BundleActivator 接口的捆綁激活器類。
捆綁包代表我們的第一塊拼圖,這裡是捆綁包的簡化表示:
為了了解 eclipse 使用的所有包,讓我們搜索所有實現 BundleActivator 接口的類。
1 2 |
from t in Types where t . Implement ( “ org . osgi . framework . BundleActivator “ ) select new { t , t . NbBCInstructions } |
誰管理捆綁包並調用 BundleActivator 方法?
要發現,讓我們搜索直接或間接調用 BundleActivator.start 的方法
1 2 3 4 |
from m in Methods let depth0 = m . DepthOfIsUsing ( “ org . eclipse . osgi . framework . internal . core . BundleContextImpl . startActivator ( BundleActivator ) “ ) where depth0 > = 0 orderby depth0 select new { m , depth0 } |
捆綁包在啟動時由 OSGi 框架激活。 框架類由 Equinox 容器啟動。 為了更好地理解容器啟動時會發生什麼,以下是容器啟動時執行的一些操作:
當容器啟動時,它初始化 OSGi 框架,框架獲取所有已安裝的包,並為每個包創建一個 BundleHost 類的實例,並將找到的包存儲到存儲庫中。
BundleHost 類實現了 Bundle 接口,其中包含啟動、停止、卸載和更新等方法,這些方法是管理 Bundle 生命週期所必需的。
所以我們的第二個拼圖是 OSGi 容器,它由 Equinoxlauncher 啟動,它初始化框架。 框架類負責加載包並激活它們。
在了解了有關 bundle 和 container 的一些基本概念之後,讓我們深入了解 bundle 並了解它們在內部是如何工作的。
讓我們以 org.eclipse.equinox.http.servlet 包為例,搜索其 Activator 類的 start 方法調用的方法。

此捆綁包創建一個服務並將其註冊到容器中。 OSGi 中的服務由標準的 Java 類或接口定義。 通常使用 Java 接口來定義服務接口。 服務是捆綁包應該用來在彼此之間進行通信的首選方法。
以下是一些使用服務的有用場景:
- 將功能從捆綁包導出到其他捆綁包。
- 從其他捆綁包中導入功能。
- 為來自其他包的事件註冊偵聽器。
上一個依賴圖中的另一個註釋是服務工廠用於創建服務實例。
我們的第三個難題是 OSGi 服務層,每個包都可以使用或聲明一些服務,強制組件設計方法,這裡是 OSGi 包的新表示。
如果 bundle 使用服務與其他 bundle 通信,它如何與其他 jar 通信?
如果我們開發一個bundle並嘗試使用另一個jar中的一個類,我們會驚訝於它不會按預期工作,原因是ClassLoader被OSGi容器鉤住了,我們來檢查一下是哪個方法調用了java。 lang.Thread.setContextClassLoader。
1 2 |
from m in Methods where m . IsUsing ( “ java . lang . Thread . setContextClassLoader ( ClassLoader ) “ ) select new { m , m . NbBCInstructions } |
許多方法調用它,包括 EquinoxLauncher。 所以每次bundle嘗試創建一個類實例時,OSGi容器都會檢查代碼是否被允許做這個動作,這就是manifest文件中導入和導出包的作用。
1 2 3 4 |
Export - Package : org . eclipse . equinox . http . servlet ; version =” 1.1.0 ″ Import - Package : javax . servlet ; version =” 2.3 ″ , javax . servlet . http ; version =” 2.3 ″ , org . osgi . framework ; version =” 1.3.0 ″ , org . osgi . service . http ; versi on =” [ 1.2 , 1.3 ) ” |
該包明確聲明了導出和導入的包,為了檢查這一點,讓我們搜索 org.eclipse.equinox.http.servlet 包使用的包並驗證它是否只使用導入的包。
1 2 |
from n in Packages where n . IsUsedBy ( “ org . eclipse . equinox . http . servlet_1 . 1.200.v20110502 “ ) select new { n , n . NbBCInstructions } |
正如我們所看到的,所有使用的包都在清單文件的導入包部分中指定。
然而,導出的包代表可以從其他包中使用的包。 它由 ExportedPackage 接口表示,我們可以使用該接口搜索容器類。
1 2 |
from t in Types where t . IsUsing ( “ org . osgi . service . packageadmin . ExportedPackage “ ) select new { t , t . NbBCInstructions } |
這個新功能的有趣之處在於,bundle 將有一個明確定義的邊界,並且它使用什麼以及它作為服務公開的內容都得到了很好的指定。
我們可以通過其他工具來強制檢查是否使用導入的包,例如使用CQLinq我們可以在每次項目使用非指定包時編寫一些規則警告,但最好在執行環境中進行此檢查,所以開發人員不能打破這些規則。
這種處理導入和導出包的能力由 OSGi 模塊層管理,這是我們的第四塊拼圖。
讓我們回到 OSGi 容器,發現它提供了哪些服務?
容器服務
正如我們在 EquinoxLauncher 類啟動容器之前發現的那樣,框架類初始化並啟動捆綁包,為了檢測提供了哪些服務,讓我們搜索框架初始化方法中使用的所有類。
1 2 |
from t in Types where t . IsUsedBy ( “ org . eclipse . osgi . framework . internal . core . Framework . initialize ( FrameworkAdaptor ) “ ) select new { t , t . NbBCInstructions } |
之前已經發現了一些類,例如 BundleRepository、BundleHost、PackageAdminImpl 和 ServiceRegistry。
其他類呢:
- 啟動級別管理器:
每個 OSGi Bundle 都與一個啟動級別相關聯,使服務器能夠控制 bundle 的相對啟動和停止順序。 只有啟動級別小於或等於服務器框架的活動啟動級別的包必須處於活動狀態。 通常,啟動級別較小的捆綁軟件往往會更早啟動。 - 安全管理員:
安全層通過將捆綁功能限制為預定義的功能來處理安全方面。 - 事件管理器:
Event Admin 服務提供了一個發布-訂閱模型來處理事件。 它是根據 OSGi 事件管理服務規範實現的。事件管理服務通過插入事件通道在事件發布者和事件訂閱者(事件處理程序)之間調度事件。 發布者將事件發佈到通道,並且事件通道定義需要通知哪些處理程序。 因此發布者和處理者之間沒有直接的了解,這簡化了事件管理。
OSGi全貌
讓我們組裝之前描述的所有拼圖,我們將擁有以下 OSGi 圖片:
這種架構有以下有趣的好處:
- 簡單的。
- 降低複雜性。
- 易於部署。
- 安全的。
是什麼讓它非常有吸引力和值得繞道而行,如果你深入研究它,你不會浪費你的時間。