深刻理解OSGI的模塊化

 

定義

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

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

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

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

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

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

OSGi的主要職責就是爲了讓開發者可以建立動態化、模塊化的Java系統。框架

OSGI框架

 

模塊和模塊化

模塊(module):定義了一個邏輯邊界,這種模塊自己精確的控制了哪些類是徹底被封裝起來的,而哪些類須要暴出來做爲外部使用。ide

模塊化(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方法也會被調用。

相關文章
相關標籤/搜索