OSGi 系列(三)之 bundle 詳解
1. 什么是 bundle
bundle 是以 jar 包形式存在的一個模塊化物理單元,里面包含了代碼,資源文件和元數據(metadata),並且 jar 包的物理邊界也同時是運行時邏輯模塊的封裝邊界。
2. MANIFEST.MF 規范
-
位置:META-NF/MANIFEST.MF
-
文件格式
- 屬性聲明的一般格式是 name: value
- 一行不超過 72 個字符,下一行則由單個空格字符開始
3. bundle 常用標識符
標識符 | 說明 |
---|---|
Bundle-Category | 描述用逗號分隔的分類名稱 |
Bundle-Activator | 啟動和停止 bundle 的類名稱 |
Bundle-Classpath | 定義用逗號分隔的路徑,包含的內容有 JAR 文件和包含類和資源的目錄(bundle內部) .點號代表 JAR 文件的根目錄,同時也是默認的 |
Bundle-Copyright | 描述 bundle 的版權信息 |
Bundle-Description | 對 bundle 的描述信息 |
Bundle-Localization | 描述 bundle 的本地文件地址,默認值是 OSGI-INF/l10n/bundle |
Bundle-ManifestVers | on |
Bundle-Name | 定義了一個具有可讀性的名字來標識 bundle。應該是一個簡短易讀沒有空格的名 |
bundle-SymbolicName | (必須)提供了 bundle 的一個全局的惟一的標志符 |
Bundle-UpdateLocation | 描述 bundle 的更新地址。bundle 需要更新,則使用這個地址進行更新。 |
Bundle-Vendor | 描述 bundle 的發行者信息。 |
Bundle-Version | 描述 bundle 的版本信息。默認值為0.0.0 |
DynamicImport-Package | 包含了一個逗號分隔的動態導入包清單 |
Export-Package | 導出包聲明 |
Import-Package | 聲明 bundle 導入的包 |
Require-Bundle | 指定 bundle 中需要其他 bundle 導出的內容 |
3.1 Bundle-SymbolicName
Bundle-SymbolicName(符號名稱) 是唯一必須指定的。通過 bundle 的符號名稱和版本號可以在框架中惟一的確定一個 bundle。也就是說,如果一個 bundle 和另外一個 bundle 有着同樣的符號名稱和版本號,那么這兩個 bundle 就是等價的。
Bundle-Name 是給用戶讀的,而 Bundle-Symbolicname 是給 OSGi 框架讀的,讓 OSGi 框架能夠唯一標識一個 bundle。
示例:
Bundle-SymbolicName: com.edu.osgi.hellowrod
3.2 Bundle-Version
格式:主版本號(Major) + 副版本號(Minor) + 微版本號(Micro) + 限定符串(Qualifier)
bundle-version: 1.0.0.bate1
- 默認值為:0.0.0
- 主版本號、副版本號、微版本號必須是數字
- 限定字符串可以為空,默認為空
- 版本號是可以進行比較的,比較是根據主版本號、小版本號、微版本號的順序進行比較。最后是字符串的限定符比較。eg:1.0.0.2016213 > 1.0.0
3.3 Export-Package
標准的 jar 文件默認公開一切內容,而 bundles 默認不公開任何內容 OSGi 通過 Export-Package 公開內容
-
可以導出多個包,多個用逗號隔開
-
可以給導出包增加任何屬性,以區分導出包
-
導出的包 version 默認為 0.0.0
Export-Package: com.edu.api Export-Package: com.edu.api;version="1.0.0" Export-Package: com.edu.api,com.edu.util;version="1.0.0"
-
當導出一個包的時候,默認導出該包的所有類、接口。可以設置過濾條件:include 包含;exclude 排除
Export-Package: com.edu.api;include:="*Service" Export-Package: com.edu.api;exclude:="*Impl"
3.4 Import-Package
OSGi 要求 bundle 顯示聲明對外部代碼的依賴:
-
不能導入 java.*
-
導入一個包,不會導入它的子包
-
通過增加屬性導入特定的包
Import-Package: com.edu.api Import-Package: com.edu.api;version="1.0.0"
-
Import-Package 版本過濾:
語法 含義 [min,max) min<=x<max [min,max] min<=x<=max (min,max] min<x<=max (min,max) min<x<max min min<=x [1.0.0,1.0.0] 精確指定一個版本 Import-Package: com.edu.api;version="[1.0.0,2.0.0)" Import-Package: com.edu.api;version="1.0.0"
-
Import-Package 屬性過濾:
Import-Package: com.edu.api;type="apache"
默認情況下 bundle2 雖然沒有聲明自定義屬性 type,但在默認情況下這並不會產生匹配沖突。如果要改變這種情況,可以使用 mandatory 附加參數,強制要求必須存在擴展屬性オ能成功匹配。
# bundle1 Import-Package: com.edu.api;type="1";mandatory:="type" # bundle2 Export-Package: com.edu.api Export-Package: com.edu.api;type="1"
注意: type 是屬性;mandatory 是 OSGi 的指令,指令前必須加 ":"
-
Import-Package 可選導入:
在大多數情況下,導入某個 Package,就說明當前這個 bundle 的正常運行是必須依賴導入的 Package 的。但還有另外一些場景導入某個 Package 是為了實現一些不影響 Bundle 正常運行的附加功能,比如 Apache thrift 里面會用到 httpclient。但是只是在特殊情況下才會用到,此時就不需要強行依賴 httpclient 了。
# resolution 可選值 mandatory(默認) 和 optional Import-Package: com.edu.api;resolution:="optional"
3.5 DynamicImport-Package
定義需要動態導入的包。這部分定義沒有在 bundle 解析過程中使用,而是在運行時動態解析並加載共享包。
動態導入和可選導入實現的功能有些類似,它們的共同特征是在 bundle 解析期間即使找不到要導入的依賴,也不會導致解析失敗。它們的區別是,動態導入每次加載包中的類都會嘗試去查找動態導入包,而可選導入包只有在 bundle 解析時才進行連接嘗試
DynamicImport-Package: *
DynamicImport-Package: com.edu.*;type=1
不推薦使用,盡量用 Import-Package。
3.6 Bundle-Classpath
Bundle-Classpath 標記有默認值 ".",它代表該 bundle 的根目錄,或者說代表該 Bundle 的 JAR 文件
-
可以設置多個,多個用逗號隔開
-
一旦定義了 Bundle-Classpath 就需要顯示加入 "."
-
按照聲明的順序搜索 bundle 類路徑條目
Bundle-Classpath: .,lib/xxx.jar
一般用於非 bundle 包的引用,不推薦使用,違反了 bundle 的使用原則。
4. 系統 bundle
- 系統 bundle 的 bundle ID 為 0
- 系統 bundle 的 getLocation() 返回的是字符串 "System Bundle"
- BundleContex.getBundle(0) 或 BundleContex.get("SystemBundle") 方法中獲取到系統 bundle 的對象實例
- 系統 bundle 的啟動級別固定為 0,且不能修改
5. bundle 依賴的常見錯誤
-
java.lang.ClassNotFoundException
bundle 沒有導入相應的包,或有導入,但被 include/exclude 限制
-
org.osgi.framework.BundleException: Unable to resolvecom.edu.osgi bank
bundle 有導入,但沒有其它的 bundle 導出該包
6. bundle 生成插件
6.1 maven-jar-plugin
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.4</version>
<configuration>
<archive>
<manifestEntries>
<Bundle-ManifestVersion>2</Bundle-ManifestVersion>
<Bundle-SymbolicName>com.github.binarylei.demo-osgi</Bundle-SymbolicName>
<Bundle-Version>1.0.0</Bundle-Version>
<Import-Package>org.osgi.framework</Import-Package>
<Bundle-Activator>com.github.binarylei.HelloBundleActivator</Bundle-Activator>
</manifestEntries>
</archive>
</configuration>
</plugin>
6.2 maven-bundle-plugin