本文是《你好,OSGi》系列的第三部分。之前介紹過OSGi是什麼,以及OSGi Bundle的使用,下面介紹OSGi依賴性管理。
OSGi依賴性管理
OSGi允許您把您的應用程序分成多個模塊,並能管理這些模塊之間的依賴性。為了達到這個目的,它引入了Bundle訪問域的概念。Bundle中類的缺省訪問范圍只對本Bundle內部可見,但對其它任何Bundle都是不可見的;在Bundle內部,類的可訪問性遵循Java語言的一般規范。那麼,您如果想要從一個Bundle中訪問另一個Bundle中的類,您應該怎麼辦呢?解決方法是將源Bundle中的包導出來,然後把它們導入到目標Bundle中。在本小結中,我們將通過一個示例程序說明這個概念。
首先,我們新建一個名com.javaworld.sample.HelloService的Bundle,並從其中導出一個包,然後將該包導入到我們的com.Javaworld.sample.HelloWorld Bundle中。
4.1. 導出Java包
我們開始新建一個com.javaworld.sample.HelloServiceBundle,並從其中導出一個Java包,具體步驟如下:
1) 新建com.javaworld.sample.HelloService Bundle,具體步驟請參見上小節中新建com.Javaworld.sample.HelloWorldBundle的步驟;
2) 在HelloService Bundle中,新建一個com.javaworld.sample.service.HelloService.Java接口,其源代碼如清單3所示。
源代碼清單3. HelloService.Java
- package com.Javaworld.sample.service;
- public interface HelloService {
- public String sayHello();
- }
3) 新建類com.javaworld.sample.service.impl.HelloServiceImpl.Java,該類實現HelloService接口,其源代碼如清單4所示。
源代碼清單4. HelloServiceImpl.Java
- package com.Javaworld.sample.service.impl;
- import com.Javaworld.sample.service.HelloService;
- public class HelloServiceImpl implements HelloService {
- public StringsayHello() {
- System.out.println("InsideHelloServiceImple.sayHello()");
- return"Say Hello";
- }
- }
4) 請在您的Eclipse Manifest編輯器中打開HelloService包中的MANIFEST.MF文件,點擊“Runtime(運行時)” 標簽,在“導出包”小節,單擊“Add(添加)”按鈕,並選擇com.Javaworld.sample.service包。這時,HelloServiceBundle中的MANIFEST.MF文件代碼應如源代碼清單5所示。
源代碼清單5. HelloService Bundle中的Manifest文件
- Manifest-Version: 1.0
- Bundle-ManifestVersion: 2
- Bundle-Name: HelloService Plug-in
- Bundle-SymbolicName:com.Javaworld.sample.HelloService
- Bundle-Version: 1.0.0
- Bundle-Vendor: JavaWORLD
- Bundle-Localization: plugin
- Export-Package: com.Javaworld.sample.service
- Import-Package:org.osgi.framework;version="1.3.0"
您可以看到,HelloService Bundle中的MANIFEST.MF文件和HelloWorldBundle非常相似,唯一的區別就是多了一個Export-Package屬性頭,該屬性頭的值為com.javaworld.sample.service;Export-Package屬性頭通知OSGi容器,其它Bundle可以從HelloService Bundle外面訪問com.Javaworld.sample.service包中的類。請注意,在示例代碼中,我們只暴露了接口類HelloService,而沒有暴露其實現類的HelloServiceImpl。
4.2. 導入Java包
下面,我們將從HelloServiceBundle中導出的com.Javaworld.sample.service包並將其導入到HelloWorldBundle中,具體步驟如下:
1). 請在com.javaworld.sample.HelloWorld Bundle中找到MANIFEST.MF文件,並在Manifest編輯器中打開,點擊“DependencIEs(依賴性)”標簽,然後點擊“ImportPackage(導入包)”按鈕,將com.Javaworld.sample.service添加為導入包,這時,您的HelloWorldBundle中的MANIFEST.MF文件內容應如源代碼清單6所示:
源代碼清單6. HelloWorld Bundle中的MANIFEST.MF文件
- Manifest-Version: 1.0
- Bundle-ManifestVersion: 2
- Bundle-Name: HelloWorld Plug-in
- Bundle-SymbolicName: com.Javaworld.sample.HelloWorld
- Bundle-Version: 1.0.0
- Bundle-Activator: com.Javaworld.sample.helloworld.Activator
- Bundle-Vendor: JavaWORLD
- Bundle-Localization: plugin
- Import-Package: com.Javaworld.sample.service,
- org.osgi.framework;version="1.3.0"
從上面的代碼可以看出,Import-Package屬性頭的值是一個由逗號分隔的字符串,這是您想導入包的列表。在HelloWorldBundle示例代碼中,我們引入了兩個包,即com.Javaworld.sample.service和org.osgi.framework。
org.osgi.framework包中包含有OSGi框架類,比如,在HelloWorldBundle中的Activator.Java中用到的BundleContext和BundleActivator類都屬於這個包。
2) 下面,請在Eclipse Java編輯器中打開com.javaworld.sample.helloworld.Activator.java,您會注意到,您現在可以訪問HelloService接口,但不能訪問HelloServiceImpl實現類,這是因為HelloServiceBunlde只導出了com.Javaworld.sampel.service包,同時HelloWorldBundle也導入了這個包。HelloServiceImpl是HelloServiceBundle的一個內部類,任何其它的Bundle都不能訪問它。
4.3. 類級別上的訪問域
如果您運行示例的HelloService服務包,它會在Eclipse控制台上打印出”HelloWorld”。但是,如果您想在HelloWorld Bundle的Activator中訪問HelloServiceImpl類,這時,編譯沒有問題,但在OSGi容器中運行這個Bundle時會拋出異常。
OSGi容器是如何能將jar文件中的一些類隱藏掉,而讓另外一些類可見呢?這是因為OSGi容器使用Java類加載器來管理類的可見性,OSGi容器為每個Bundle創建不同的類加載器,因此每個Bundle能訪問位於下列位置中的類:
a) 位於Java啟動類路徑下的、所有以Java.*開頭的包中的類;
b) 位於OSGi框架類路徑下的類,通常有一個獨立的類加載器負責加載框架的實現類及關鍵的接口類;
c) 位於Bundle空間中的類,這些類通常包含在與Bundle相關的jar文件中,以及加到這個Bundle中的其它jar包中的類。
d) 導入包中的類,例如,HelloWorld Bundle導入了com.javaworld.sample.service包,因此它能訪問該包中的類。Bundle級別的訪問域是OSGi一個非常強大的功能,例如,它可以讓您安全地更新HelloServiceImpl.Java類,而不必擔心依賴於這個類的代碼受到破壞。
以上就大概介紹了OSGi依賴性管理的概念。