深入理解OSGI的模塊化

定義

OSGIOpen Service Gateway Initiative)技術是面向Java的動態模型系統。

OSGI框架實現了一個優雅、完整和動態地組件模型。應用程序(bundle)無需重新引導可以被遠程安裝、啓動、升級和卸載。

OSGi技術提供允許應用程序使用精煉、可重用和可協作的組件構建的標準化原語。 這些組件能夠組裝進一個應用和部署中。

OSGi服務平臺提供在多種網絡設備上無需重啓的動態改變構造的功能。

爲了最小化耦合度和促使這些耦合度可管理,OSGi技術提供一種面向服務的架構,它能使這些組件動態地發現對方。   

OSGi聯盟已經開發了例如像HTTP服務器、配置、日誌、安全、用戶管理、XML等很多公共功能標準組件接口。這些組件的兼容性插件實現可以從進行了不同優化和使用代價的不同計算機服務提供商得到。然而,服務接口能夠基於專有權基礎上開發。

OSGi的主要職責就是爲了讓開發者能夠創建動態化、模塊化的Java系統。

OSGI框架

 

模塊和模塊化

模塊(module):定義了一個邏輯邊界,這種模塊本身精確的控制了哪些類是完全被封裝起來的,而哪些類需要暴出來作爲外部使用。

模塊化(modularity):將一個大型系統分解爲多個較小的互相協作的邏輯單元,通過強制設置模塊之間的邏輯邊界來改善系統的維護性和封裝性。

構成

OSGi框架從概念上可以分爲三層:模塊層、生命週期層和服務層。

Module Layer:模塊層關注代碼的打包和共享;

Lifecycle Layer:生命週期層提供運行時管理以及對OSGI框架的訪問接口;

Service Layer:服務層關注模塊之間的交互和通信。

 

模塊層

模塊層是 OSGi 框架中最基礎的部分。

OSGi 的模塊化,是通過爲 Jar 包添加metadata 來定義哪些類該暴露,哪些類該隱藏,其控制單元叫做 Bundlejar 包)。

首先,必須先了解一個基本概念——什麼是Bundle

bundle 是以 jar 包形式存在的一個模塊化物理單元,裏面包含了代碼,資源文件和元數據(metadata),並且jar包的物理邊界也同時是運行時邏輯模塊的封裝邊界。

 

如何定義Bundle

 

Bundle  OSGi 中的基本組件,其表現形式仍然爲 Java 概念中傳統的 Jar 包。

通過 META-INF 目錄下的 MANIFEST.MF 文件對其予以進一步的定義。

通常一個 MANIFEST.MF 文件的內容如下:

 

Manifest-Version: 1.0

Bundle-ManifestVersion: 2

Bundle-Name: Util

Bundle-SymbolicName: com.ibm.director.la.util

Bundle-Version: 1.0.0

Bundle-RequiredExecutionEnvironment: J2SE-1.5

Import-Package: org.osgi.framework;version="1.3.0"

Export-Package: com.ibm.director.la.util;uses:="org.osgi.framework"

Bundle-ClassPath: lib/junit.jar,

 

MANIFEST.MF 文件存儲的實際上是 Bundle 的元數據。

元數據的內容可以精確的定義 Bundle 的各種特徵,同時能更好的對 Bundle 進行標識同時幫助用戶對Bundle進行理解。

 

MANIFEST.MF文件格式

1.         屬性聲明的一般格式:namevalue

2.         一行不超過72個字符,下一行繼續則由單個空格字符開始

3.         每個子句(clause       進一步分解爲一個目標(target)和一組由分號分隔的name-value對參數(parameter

 

元素解釋:

Bundle-SymbolicName  唯一的bundle名稱,相當於在系統中的idsingleton表示是否使用單啓動方式 #可選的

 

Bundle-Version  主要的版本號

Bundle-ManifestVersion  定義了bundle遵循規範的規則,1表示r3規範 2表示r4和以後的版本

a)         唯一有效的值是2

b)         沒有Bundle-ManifestVersionBundle不要求指定Bundle- SymbolicName屬性

 

Bundle-Name   bundel名稱

Bundle-Vendor  發佈商

Bundle-RequiredExecutionEnvironment  需要的執行環境

Build-Jdk  jdk版本

Created-By  創建者

Bundle-Activator  Activator類路徑

Import-Package  引用包的信息,包括包名稱和版本號,只有引用了這些包,才能讓classloader裝載

a)         導入一個包並沒有導入它的子包

b)         Import-Package通過屬性導入特定的包

c)         java.*

d)         對於任意屬性,OSGI只支持相等匹配

e)         Version及其值的格式是OSGI規範所定義,支持更加靈活的匹配方法

 

f)          需要指定一個精確的版本範圍,使用“[1.0.1,2.0.1]”這樣的格式

g)         當沒有指定版本範圍時,默認的值是“0.0.0

 

Export-Package  對外暴露的Package

a)         標準Jar文件默認公開一切內容,而Bundle中默認不公開任何內容

b)         可導出多個包,用逗號分隔

c)         可以給導出包增加任意屬性

d)         可以給導出包設置Version,默認爲0.0.0

        

 

Require-Bundle  直接引用整個bundle

Bundle-ClassPath  Bundle  Classpath,內部類路徑

Fragment-Host  Fragment 類型 Bundle 所屬的 Bundle

DynamicImport-Package  Bundle動態引用的 package

 

OSGI類的查找順序

1.         如果類所在的包以“java.”開頭,委託給父類加載器

2.         如果類所在的包在導入包中,委託給導出該包的Bundle

3.         Bundle自身的類路徑上查找

 

依賴解析

含義

1.         只有滿足所有的依賴(Import-Package),bundle纔可用

2.         OSGI框架的一個最重要任務之一就是:通過自依賴解析自動化地進行依賴管理

 

依賴解析規則

1           級聯解析

2           Import-Package的屬性約束和版本約束

3           多個Bund滿足Import-Package依賴(多個Provider)時:

3.1     已解析的(resolvedbundle優先級高,未解析的(installedbundle優先級低

3.2     相同優先級,有多個匹配時,版本高者優先,版本相同則選最先安裝的bundle

4           一個bundle只能看到某個package的唯一一個實例

5           uses 子句

5.1     用於限制Export-Package

5.2     需要用到uses子句的場景

5.2.1     導出包中的類,其方法簽名中包含了其Import-Package中的類

5.2.2     導出包中的類,繼承了其Import-Package中的類

5.3     users約束是可傳遞的

5.4     謹慎使用uses,大大·限制解析的靈活性

 

生命週期層

作用

1           在應用程序外部,生命週期層精確低定義了對bundle生命週期的相關操作

2           對生命週期的操作,允許你動態地改變進行於框架中的bundle組成,並以此來管理和演化應用程序

3           在應用程序內部,生命週期層定義了bundle訪問其執行上下文的方式,爲bundle提供了一種與OSGI框架交互的途徑以及一些執行時的便利條件

4           OSGI框架支持對bundle形式的JAR文件實現全生命週期管理,包括:安裝、解析、啓動、停止、更新和卸載

5           運行時生命週期管理,“動態類路徑”

 

下圖爲 Bundle 生命週期的狀態轉移圖:

 

 

重要接口

生命週期層的API主要是由以下三個核心接口來組成的:

BundleActivatorBundleContext  Bundle

 

BundleActivator:讓你能夠捕捉bundlestartstop事件,並對這兩個事件作出自定義的反應。

其中:

1           調用start()方法的激活器實例與調用stop()的實例是同一個

2           stop()方法被調用之後,激活器實例就被丟棄並不再不用

3           如果一個bundle被停止後,又重新啓動,那麼將創建一個新的激活器實例,同時它的start()方法和stop()方法也將被適時觸發。

 

BundleContext:一個bundle在框架中的執行時上下文,這個上下文提供了和框架進行交互的方法。

其中:

1           bundle屬於active狀態時,BundleContext纔有意義,即start()方法被調用和stop()方法被調用之間的時間點

2           註冊服務

方法如下:

public ServiceRegistration registerService(String clazz, Object service,

                            Dictionary properties);

 

調用例子:

@Override

    public void start(BundleContext context) throws Exception {

        Dictionary<String, String> props = new Hashtable<String, String>();

        props.put("ServiceName", "Calculation");

        context.registerService(ICalculation.class.getName(), new Calculation(), props);

        System.out.println("Service registered!");

    }

3           獲取服務

有幾種方式:

1ServiceReference ref = context.getServiceReference(LogService.class.getName());

優點:很難說有什麼優點,硬要說幾句的話,那就是邏輯夠簡單,調用最少,適合一次性操作。
缺點:需要判斷返回值是否爲null,需要手動申請和釋放service,由於OSGi的動態性,請在獲取ref後儘快使用,無法保證ref長期有效。每次訪問都會有service獲取和釋放的開銷。
用途:適合於不頻繁的調用service,且在service不可用時也能繼續執行後續操作的場景。

 

2使用ServiceListener

優點:只在Service變更時產生一次service獲取開銷,動態感知service的註冊和註銷。
缺點:在ServiceListener註冊之前已經存在的Service無法監聽到。需要自己維護service的獲取和釋放。在需要監聽多個Service實例時,使用並不方便。

 

3使用ServiceTracker

ServiceTracker其實是對ServiceListener實現方式的封裝,使得對service的獲取更加簡潔,同時也解決了不能監聽到已經存在的Service的問題(其實就是在增加ServiceListener的同時調用BundleContext.getAllServiceReferences方法以獲取現有的Service引用)

 

有一點需要注意的是,tracker需要調用open方法才能監聽到Service,另外,在bundle stop以後,bundleopenServiceTracker不會自動關閉,所以一定不要忘記在bundle結束之前,關閉所有在bundleopenServiceTracker

 

4、使用OSGI Blueprint

如下

 

 

 

Bundle:在邏輯上表示了一個bundleOSGi環境中的一個物理bundle對應了一個bundle對象。該對象中包含了bundle的基本信息和bundle生命週期的控制接口。

 

 

啓動級別

1、啓動級別的數值越高,啓動順序越靠後

2、只有System Bundlebundle ID0)的啓動級別可以爲0,其他Bundle的啓動級別都大於0,最大值爲Integer.MAX_VALUE

3、動態啓動級別

 

系統Bundle

啓動過程:Bundlestart()方法爲空操作,因爲OSGI框架一啓動。系統Bundle就已經啓動了

停止過程:Bundlestop()方法會立即返回並在另外一條線程中關閉OSGI框架

更新過程:Bundleupdate()方法會立即返回並在另外一條線程中重啓OSGI框架

卸載過程:系統Bundle無法卸載,如果執行了Bundleuninstall()方法,那麼框架會拋出一個BundleException異常

 

Bundle刷新流程

從某一bundle開始計算受影響的bundle有向圖

處於Active狀態的bundle被停止並被切換至Resolved狀態

處於Resolved狀態的bundle,切換至Installed狀態,這些bundle的依賴關係不再被解析

處於uninstalled狀態的bundle會被從圖中移除,同時也會被徹底地從框架中移除(由GC回收)

其他bundle,如果框架重啓之前處於Active狀態,重啓前框架會對這些bundle以及其所依賴的bundle進行解析

當所有的工作完成之後,框架會觸發一個FrameworkEvent.PACKAGES_REFRESHED事件

 

服務層

面向服務的設計

1、降低服務提供者和使用者之間的耦合,這樣更容易重用組件

2、更強調接口而不是實現類

3、清晰描述依賴關係,讓你知道一切是如何結合在一起的(可以有附加的元數據描述)

4、支持多個相互競爭的服務實現,這樣你可以互換這些實現(動態替換)

 

OSGI服務

OSGI框架擁有一個集中的服務註冊中心,它遵循發佈-查詢-綁定模型

1、提供者bundle可以將POJOs發佈爲服務。

1.1、註冊的時候可以設置這個 Service 的屬性。而在獲取 Service的時候可以根據屬性進行過濾。

1.2、爲了讓別的bundle能發現這個服務,你必須在發佈它之前對其進行特徵描述。這些特徵包括接口的名字(可以是名字的數組),接口的實現,和一個可選的java.util.Dictionary類型的元數據信息。

2、使用者bundle可以找到並綁定服務

3、服務註冊、更新、註銷

4、服務註冊對象是私有的,不能被別的bundle共享,它們與發佈服務的bundle的生命週期是綁定的

5OSGI將會接受以具體類名註冊的服務,但是不推薦這樣做

6、當一個bundle停止時,任何沒有被移除的服務都會被框架自動移除。當bundle停止時,不必明確地註銷服務。

7、服務監聽

8、服務追蹤器 – ListenerServiceTracker

9、服務工廠 – 爲不同的bundle提供相同服務的不同實例

10、配置管理:可將配置文件放置/etc下,隨着配置文件的更改,ManagedService接口的實現類的updated方法也會被調用。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章