在目前的Java開發平臺中,對于組件開發過程,比如打包、部署和驗證等,并沒有一個統一的標準。正因如此,許多Java項目,例如JBoss" />

亚洲免费在线-亚洲免费在线播放-亚洲免费在线观看-亚洲免费在线观看视频-亚洲免费在线看-亚洲免费在线视频

探索 OSGi 框架的組件運行機制

系統 2314 0

?

OSGi 框架為基于 Java 的組件開發提供了一套通用的和標準的解決方案,通過 OSGi 框架可以輕松實現組件信息的隱藏和共享。本文介紹了 OSGi 框架中的組件(Bundle)的運行機制,并結合實際示例加以說明,讀者可以進一步深入了解 OSGi 框架的基本原理,并解決實際開發工作中遇到的類似問題。
<!----><!----><!---->

在目前的 Java 開發平臺中,對于組件開發過程,比如打包、部署和驗證等,并沒有一個統一的標準。正因如此,許多 Java 項目,例如 JBoss 和 Net Beans,都擁有一套自定義的組件開發規范和框架,但是實際上這些解決方案都是基于為組件分配獨立的類加載器 (Class Loader) 的思想。 OSGi 框架為基于 Java 的組件開發提供了一套通用的和標準的解決方案,并已經成為事實上的工業標準。

OSGi 組件框架

在 OSGi 框架中,組件被稱為 Bundle,它是由一些提供 Bundle 自身功能的資源 ( 如 Java 類文件、配置文件等 )、MANIFEST.MF 文件和其它資源文件構成。在運行時環境中,每個 Bundle 都有一個獨立的 Class Loader,Bundle 之間通過不同的類加載器和在 MANIFEST.MF 文件中定義的包約束條件就可以輕松實現包級別的資源隱藏和共享,從而實現基于組件方式的開發和運行。 Class Loader 是實現這種運行方式的關鍵機制,每個 Bundle 的 Class Loader 必須在此 Bundle 得到正確的解析 ( Resolving ) 之后,再由 OSGi 框架創建。只有當一個 Bundle 中的所有包約束條件都滿足時,它才被正確解析完畢。

Bundle 解析

Bundle 的解析是通過分析定義在 MANIFEST.MF 文件中的元數據 ( 主要定義了包約束條件 ),查找與包約束條件相匹配的 Bundle 并建立關聯關系的過程。 MANIFEST.MF 文件中的包約束條件主要是通過 Import-Package、DynamicImport-Package、Export-Package 和 Require-Bundle 這四種表達方式來實現。下面簡單介紹一下它們:

  1. Import-Package:定義需要導入的包。默認是所有需導入的包必須都能夠找到相應的導出 Bundle (Exporter),否則解析失敗。
  2. Export-Package:定義導出的包。在一個 Bundle 里面,一個包的導出定義并不意味著相應的包導入定義,而是這些類資源會在 Bundle 自身的類路徑里面查找和加載。
  3. Require-Bundle:定義依賴的 Bundle 。
  4. DynamicImport-Package:定義需要動態導入的包。這部分定義沒有在 Bundle 解析過程中使用,而是在運行時動態解析并加載共享包。

在 Bundle 得到正確解析后,OSGi 框架將會生成此 Bundle 的依賴關系表。在實際運行過程中,框架就可以通過此關系表找到 Bundle 依賴的外部 Class Loader,從而實現外部類資源的加載和運行 hg 。 Bundle 的關系圖可以在 OSGi 的控制臺中通過內部命令" bundle "來查看,如下圖所示:


圖 1. Bundle 依賴關系表
圖 1. Bundle 依賴關系表

點擊這里 查看清晰大圖。)

Bundle 運行

Bundle 的運行主要依靠于 OSGi 框架為其創建的類加載器(Class Loader),加載器負責查找和加載 Bundle 自身或所依賴的類資源。 ClassLoader 之間的依賴關系構成了一個有向圖,如下圖所示:


圖 2. Class Loader 依賴關系圖
圖 2. Class Loader 依賴關系圖

Bundle 的 Class Loader 能加載的所有類的集合構成了 Bundle 的類空間 (Class Space) 。類空間包含的類資源主要來自于以下幾個方面:

  1. 父 Class Loader 可加載的類集合;
  2. Import-Package 定義的依賴的包;
  3. Require-Bundle 定義的依賴的 Bundle 的類集合;
  4. Bundle 自身的類集合,通常在 Bundle-Classpath 中定義;
  5. 隸屬于 Bundle 的 Fragment 類集合。

在實際運行環境中,Bundle 的 Class Loader 根據如下規則去搜索類資源。規則簡要介紹如下:

  1. 如類資源屬于 java.* 包,則將加載請求委托給父加載器;
  2. 如類資源定義在 OSGi 框架中啟動委托列表(org.osgi.framework.bootdelegation)中,則將加載請求委托給父加載器;
  3. 如類資源屬于在 Import-Package 中定義的包,則框架通過 Class Loader 依賴關系圖找到導出此包的 Bundle 的 Class Loader,并將加載請求委托給此 Class Loader ;
  4. 如類資源屬于在 Require-Bundle 中定義的 Bundle,則框架通過 Class Loader 依賴關系圖找到此 Bundle 的 Class Loader,將加載請求委托給此 Class Loader ;
  5. Bundle 搜索自己的類資源 ( 包括 Bundle-Classpath 里面定義的類路徑和屬于 Bundle 的 Fragment 的類資源);
  6. 若類在 DynamicImport-Package 中定義,則開始嘗試在運行環境中尋找符合條件的 Bundle 。

如果在經過上面一系列步驟后,仍然沒有正確地加載到類資源,則 OSGi 框架會向外拋出類未 發現異常。

確保 Bundle 類空間的完整性

從上述對 OSGi 組件框架的剖析中,MANIFEST.MF 文件中定義的元數據對 Bundle 的解析和運行起著至關重要的作用。 Bundle 所需要的類資源應該完全被其類空間所覆蓋,否則將會在運行時環境中拋出類或資源未發現異常,從而導致 Bundle 無法正常工作。下面給出一個實際例子來說明這種情況。

在 Eclipse 環境中創建兩個插件開發項目 , 并命名為 Client 和 HelloService. HelloService 向外提供服務 , Client 通過注冊在 OSGi 控制臺中的命令來調用 HelloService 的服務。 HelloSerive 定義的服務接口為 :


清單 1. IHello.java

                            
package com.ibm.helloservice; 
public interface IHello { 
    public void sayHello(String addition); 
}
 
          



清單 2. ILabelProvider.java

                            
package com.ibm.helloservice; 

public interface ILabelProvider { 
    public String getLabel(); 
} 

          

?

同時 HelloService 還給出了具體實現 :


清單 3. HelloImpl.java 和 Resource.java

                            
package com.ibm.helloservice.impl; 
import com.ibm.helloservice.IHello; 
public class HelloImpl implements IHello { 
    public void sayHello(String addition) { 
        System.out.println ("Hello ” + addition + "!"); 
    } 
} 

package com.ibm.helloservice.resource; 
import com.ibm.helloservice.ILabelProvider; 
public class Resource implements ILabelProvider { 
    public String getLabel() { 
        return "Test Label"; 
    } 
}

          

?

由于此 Bundle 向外提供了服務和資源信息,因此需要在 MANIFEST.MF 文件中將它們所屬于的包導出:


清單 4. MANIFEST.MF

                            
 Manifest-Version: 1.0 
 Bundle-ManifestVersion: 2 
 Bundle-Name: HelloService Plug-in 
 Bundle-SymbolicName: HelloService 
 Bundle-Version: 1.0.0 
 Bundle-Activator: com.ibm.helloservice.impl.Activator 
 Bundle-Localization: plugin 
 Export-Package: com.ibm.helloservice, 
 com.ibm.helloservice.resource 
 Import-Package: org.osgi.framework; version="1.3.0"
 
          

?

在 BundleActivator 類中,向 OSGi 框架注冊了 IHello 的 service,具體實現代碼十分簡單,在此略過。服務使用方 (Client Bundle),是一個控制臺程序,通過注冊在 OSGi 框架中的命令調用 HelloService Bundle 提供的服務。具體實現參見如下代碼:


清單 5. Activator.java

                            
package com.ibm.client; 

import java.io.IOException; 
import java.io.InputStream; 
import java.util.Properties; 

import org.eclipse.osgi.framework.console.CommandInterpreter; 
import org.eclipse.osgi.framework.console.CommandProvider; 
import org.osgi.framework.BundleActivator; 
import org.osgi.framework.BundleContext; 
import org.osgi.framework.ServiceReference; 
import org.osgi.framework.ServiceRegistration; 
import org.osgi.util.tracker.ServiceTracker; 
import org.osgi.util.tracker.ServiceTrackerCustomizer; 

import com.ibm.helloservice.IHello; 
import com.ibm.helloservice.ILabelProvider; 

public class Activator implements BundleActivator, 
    ServiceTrackerCustomizer, CommandProvider  { 

    private ServiceTracker helloServiceTracker; 
    private ServiceRegistration registration; 
    private BundleContext bundleContext; 
    private IHello hello; 
	
    private static Properties config = null; 

    static { 
        config = new Properties(); 
        try { 
            InputStream is = Activator.class.getClassLoader() 
                .getResourceAsStream("LabelProvider.properties"); 
            config.load(is); 
        } catch (IOException e) { 
            e.printStackTrace(); 
        } 
    } 
	
    public void start(BundleContext context) throws Exception { 
        bundleContext = context; 
        helloServiceTracker = new ServiceTracker(context, IHello.class.getName(), this); 
        helloServiceTracker.open(); 
        registration = context.registerService(CommandProvider.class.getName(), 
        this, null); 
    } 

    public void stop(BundleContext context) throws Exception { 
        helloServiceTracker.close(); 
        registration.unregister(); 
        bundleContext = null; 
    } 

    public Object addingService(ServiceReference servRef) { 
        Object service = bundleContext.getService(servRef); 
        if(service instanceof IHello) { 
            hello = (IHello)service; 
            return hello; 
        } 	
        return service; 
    } 

    public void modifiedService(ServiceReference servRef, Object service) { } 

    public void removedService(ServiceReference servRef, Object service) { 
        if(service instanceof IHello) { 
            hello = null; 
        } 
        bundleContext.ungetService(servRef); 
    } 

    public String getHelp() { 
        return "\tcall - say hello"; 
    } 

    public void _call(CommandInterpreter ci) throws Exception { 
        hello.sayHello(newLabelInstance().getLabel()); 
    } 
	
    private ILabelProvider newLabelInstance() throws Exception { 
        String className = config.getProperty("ProviderClass"); 
        Class<?> labelClass = this.getClass().getClassLoader().loadClass(className); 
        ILabelProvider label = (ILabelProvider)labelClass.newInstance(); 
        return label; 
    } 
 }
          

?

在以上代碼中,Bundle 通過 ServiceTracker 得到 IHello 這個服務接口,并同時向 OSGi 框架注冊一個名為" call "的命令。在這個命令的執行過程中,Bundle 通過自身的 ClassLoader 去加載定義在 LabelProvider.properties 文件中的 ILabelProvider 實現類,并打印 Label Provider 中的內容。從以上代碼中,我們會很容易地定義出此 Bundle 需要導入哪些包,如下 MANIFEST.MF 文件所示 :


代碼清單 6: MANIFEST.MF

                            
 Manifest-Version: 1.0 
 Bundle-ManifestVersion: 2 
 Bundle-Name: Client Plug-in 
 Bundle-SymbolicName: Client 
 Bundle-Version: 1.0.0 
 Bundle-Activator: com.ibm.client.Activator 
 Bundle-Localization: plugin 
 Import-Package: com.ibm.helloservice, 
 org.eclipse.core.runtime, 
 org.eclipse.osgi.framework.console;version="1.0.0", 
 org.osgi.framework;version="1.3.0", 
 org.osgi.util.tracker;version="1.3.1"
          

?

從表面上看 , 這個文件沒有任何遺漏 , 在 Eclipse IDE 環境中能夠正確解釋通過 , 并且在啟動 OSGi 框架后 , Client Bundle 也能夠被正確解析和啟動。但是在敲入 call 命令的后 , 就會顯示如下錯誤 :


圖 3. 資源加載異常
圖 3. 資源加載異常

點擊這里 查看清晰大圖。)

原來缺少了一個依賴包 com.ibm.helloservice.resource.Resource。在 MANIFEST.MF 文件中加入對它的定義后 , call 命令就能夠正確運行了 :


圖 4. 運行結果
圖 4. 運行結果

這個例子模擬了在實際 Bundle 開發過程中普遍遇到的問題,即許多類資源定義在 plugin.xml 文件中,然后由其他組件動態的創建和調用,而在 Eclipse IDE 開發環境中,并沒有相應的機制去檢查非 Java 代碼里的包引用,從而 Bundle 缺失對某些包依賴的定義,最后就造成了 Bundle 類空間的不完整性。通常這種情況下,Bundle 能夠被正確解析,但是在運行的時候就會拋出類未找到異常。所以在開發一個 Bundle 的時候,一定要仔細檢查類空間是否已經將整個 Bundle 所需要的類資源覆蓋,這樣才能避免運行時的異常發生。

合理使用 Manifest 文件

Import-Package 與 Require-Bundle

OSGi 框架提供了兩種導入包依賴的方式,即 Import-Package 和 Require-Bundle 。從下圖中我們可以看出 Require-Bundle 會對整個 Bundle 產生依賴,也就是說 Bundle 所 Export 出的包都會被 A 加入到自己的類空間,而 Import-Package 只會對指定的包產生依賴關系。


圖 5. Bundle 依賴關系圖
圖 5. Bundle 依賴關系圖

在大多數情況下,都應該使用 Import-Package 而不是 Require-Bundle 。 Import-Package 比 Require-Bundle 更利于 Bundle 的部署和版本維護,同時在查找類的時候有更高的效率。

Eclipse-LazyStart

在 OSGi R4 版本中,通過對 Eclipse-LazyStart 屬性的設置,可以指定 Bundle 是否支持慢啟動功能。當 Eclipse-LazyStart 被設置為 true 的時候,Bundle 不會被默認啟動,而是當 Bundle 的類或其它資源被第一次調用時,由框架自動激活 Bundle 。這樣就會使得 OSGi 框架在啟動的時候,只啟動必須的 Bundle 而延遲啟動其它的 Bundle,從而確保了啟動時間和效率。在默認情況下,Eclipse-LazyStart 為 false 。

Bundle-ManifestVersion

Bundle-ManifestVersion 指定了 Bundle 應該遵循 OSGi 規范的版本號。其默認值是 1,即 R3 版本;值為 2 的時候,表示為 R4 版本。當 Bundle 需要用到 R4 中新功能的時候,如 Bundle 的慢啟動,則必須顯示設置 Bundle-ManifestVersion 為 2 。

類不一致性問題

在 OSGi 框架中,多個 Bundle 可以導出相同的包,如果在某個 Bundle 的類空間中存在來自于不同 Bundle 的相同類信息,就會導致類的不一致性問題。示例如下:


圖 6. Bundle 依賴關系圖
圖 6. Bundle 依賴關系圖

Bundle A 向外 Export 兩個包 p 和 r,其中 p 對 q 存在約束關系,即 q 須為 Bundle B 中的 1.0 版本。同時,Bundle C 又分別導入了 p 和 q 兩個包,包 p 來自于 A,而包 q 為 Bundle D 中的 2.0 版本。雖然 C 中并沒有顯示地定義對 B 中包 q 的依賴關系,但是由于 A 中的包 p 綁定了 B 中版本為 1.0 的包 q,故 C 在解析對包 p 的依賴關系的時候也會自動把 B 中 1.0 版本的包 q 導入到自己的類空間中。這樣在 C 的類空間中,就存在著兩個不同版本、來自于不同 Bundle 的包 q,進而就會存在兩個不同的 Class Loader 對應著包 q,故在 Bundle C 的運行過程中就會出現類不一致性的異常。

小結

本文介紹了 OSGi 框架的組件運行機制,包括 Bundle 的解析、運行等,并結合實際的示例演示了在基于 OSGi 平臺開發 Bundle 的過程中應該注意的一些問題。

?

探索 OSGi 框架的組件運行機制


更多文章、技術交流、商務合作、聯系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯系: 360901061

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦!!!

發表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 欧美中文综合在线视频 | 久久一级片 | 国产精品视频国产永久视频 | 尹人香蕉99久久综合网站 | 国产精品中文字幕在线观看 | 九九久久99综合一区二区 | 亚洲精品国产乱码在线播 | 大学生一级黄色片 | 一级做a爱片特黄在线观看免费看 | 日本人一级毛片免费完整视频 | 激情五月婷婷久久 | 男女很黄很色床视频网站免 | 日本香蕉视频 | 在线视频中文 | 日本在线色视频 | 日日摸夜夜添夜夜添影院视频 | 伊人久久成人爱综合网 | 丁香色综合 | 国产亚洲精品美女久久久久 | 综合伊人久久 | 高清黄色直接看 | 国产伦精品一区二区三区高清 | 久草视频播放 | 中文无码久久精品 | 久热精品视频在线 | 国产成人精品高清在线观看99 | 亚洲美女亚洲精品久久久久 | 一a一片一级一片啪啪 | 国产97色在线 | 免费 | 狠狠色综合久久婷婷 | 91在线资源| 久久semm亚洲国产 | 精品欧美一区二区三区四区 | 欧美一级黄色毛片 | 老司机观看精品一区二区 | 国产精品成人免费视频不卡 | 久久不卡一区 | 欧美综合区 | 四虎永久免费地址在线观看 | 91久久夜色精品国产网站 | 天天看片夜夜爽 |