程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> J2EE >> OSGi服務:非常適合SOA的架構

OSGi服務:非常適合SOA的架構

編輯:J2EE

本文是《你好,OSGi》系列的第四部分。下面講述OSGi服務。對OSGi不是很了解的讀者可以先閱讀OSGi是什麼一文。

OSGi服務

前面我們提到,OSGi架構非常適合我們實現面向服務的應用(SOA)。它可以讓Bundles導出服務,而其它的Bundles可以在不必了解源Bundles任何信息的情況下消費這些導出的服務。由於OSGi具有隱藏真實的服務實現類的能力,所以它為面向服務的應用提供了良好的類與接口的組合。

在OSGi框架中,源Bundle在OSGi容器中注冊POJO對象,該對象不必實現任何接口,也不用繼承任何超類,但它可以注冊在一個或多個接口下,並對外提供服務。目標Bundle可以向OSGi容器請求注冊在某一接口下的服務,一旦它發現該服務,目標Bundle就會將該服務綁定到這個接口,並能調用該接口中的方法。下面我們舉個例子,以便我們能更好理解與OSGi相關的這些概念。

5.1. 導出服務

在本小節中,我們將更新HelloService Bundle,以便它能把HelloServiceImpl類的對象導出為服務,具體步驟如下:

1) 修改com.Javaworld.sample.HelloService Bundle中的MANIFEST.MF文件,讓它導入org.osgi.framework包(譯者注,這一步我們已經完成);

2) 新建Java類com.javaworld.sample.impl.HelloServiceActivator.Java,其源代碼如清單7所示;

源代碼清單7. HelloServiceActivator.Java

  1. public class HelloServiceActivator implements BundleActivator {
  2. ServiceRegistrationhelloServiceRegistration;
  3. public void start(BundleContext context)throws Exception {
  4. HelloService helloService = newHelloServiceImpl();
  5. helloServiceRegistration=context.registerService(HelloService.class.getName(), helloService, null);
  6. }
  7. public void stop(BundleContext context)throws Exception {
  8. helloServiceRegistration.unregister();
  9. }
  10. }

請注意,在源Bundle中,我們應使用BundleContext.registerService()方法導出服務,這個方法帶三個參數:

a) 該方法第一個參數為您要注冊的服務的接口名稱。如果您想把您的服務注冊到多個接口下,您需要新建一個String數組存放這些接口名,然後把這個數組作為第一個參數傳給registerService()方法。在示例代碼中,我們想把我們的服務導出到HelloServer接口名下;

b) 第二個參數是您要注冊的服務的實際Java對象。在示例代碼中,我們導出HelloServiceImpl類的對象,並將其作為服務;

c) 第三個參數為服務的屬性,它是一個Dictionary對象。如果多個Bundle導出服務的接口名相同,目標Bundle就可以使用這些屬性對源Bundle進行過濾,找到它感興趣的服務。

3) 最後,請修改HelloServiceBundle中的MANIFEST.MF文件,將Bundle-Activator屬性頭的值改為com.Javaworld.sample.service.impl.HelloServiceActivator。

現在HelloService Bundle就可以導出HelloServiceImpl對象了。當OSGi容器啟動HelloServiceBundle時,它會將控制權交給HelloServiceActivator.Java類,HelloServiceActivator將HelloServiceImpl對象注冊為服務。下面,我們開始創建該服務的消費者。

5.2. 導入服務

在本小節中,我們將修改上面開發的HelloWorld Bundle,以便讓它成為HelloService服務的消費者。您主要需要修改HelloWorldBundle中的Activator.Java代碼,修改後的代碼如源代碼清單8所示:

源代碼清單8. HelloWorld Bundle中的Activator.Java

  1. packagecom.Javaworld.sample.helloworld;
  2. importorg.osgi.framework.BundleActivator;
  3. importorg.osgi.framework.BundleContext;
  4. importorg.osgi.framework.ServiceReference;
  5. importcom.Javaworld.sample.service.HelloService;
  6. publicclass Activator implements BundleActivator {
  7. ServiceReference helloServiceReference;
  8. public void start(BundleContext context)throws Exception {
  9. System.out.println("HelloWorld!!");
  10. helloServiceReference=context.getServiceReference(HelloService.class.getName());
  11. HelloService helloService=(HelloService)context.getService(helloServiceReference);
  12. System.out.println(helloService.sayHello());
  13. }
  14. public void stop(BundleContext context)throws Exception {
  15. System.out.println("Goodbye World!!");
  16. context.ungetService(helloServiceReference);
  17. }
  18. }

在上面的代碼中,BundleContext.getServiceReference()方法將為注冊在HelloService接口下的服務返回一個ServiceReference對象。如果存在多個HelloService服務,該方法會返回排行最高的服務(服務的排行是通過Constants.SERVICE_RANKING屬性指定的)。您一旦獲得ServiceReference對象,您就可以調用其BundleContext.getService()方法獲取真實的服務對象。

您可以參照運行Bundle的方法運行上面的示例應用,請點擊“RunàRun…”菜單,並確保HelloWorld和HelloService這兩個Bundle被選中。當您啟動HelloServiceBundle時,您會在控制台上看到“InsideHelloServiceImple.sayHello()”,這個消息是由HelloServiceImpl.sayHello()方法打印出來的。

5.3. 創建服務工廠

在上節中,我們學會了如何使用OSGi框架新建一個Java對象,並把它注冊為一個服務,然後讓其它的Bundle去消費這個服務。如果您看一下HelloServiceActivator.start()方法,您會注意到我們在start()方法中新建了HelloServiceImpl類對象,然後將它注冊到HelloService接口名下。這樣注冊後,任何其它的Bundle在請求HelloService服務時,OSGi容器將返回同一對象。

在大多數情況下,這樣的實現方法沒有問題。但是,比如說我們要為每一個Bundle消費者返回不同的HelloServiceImpl對象,再比如說,您的服務對象要提供的服務為打開一個數據庫連接,但並不是馬上就打開它,而是在真正需要的時候才打開這個數據庫連接。

對這兩種情況,我們的解決方法是,新建一個類實現ServiceFactory接口,並把該類的對象注冊為服務,但並不是注冊實際的服務對象。一旦您完成這一步,其它Bundle在請求該服務時,您的ServiceFactory實現類將接管該請求,ServiceFactory會為每個Bundle新建一個服務對象,並將真實服務的創建時間延遲到有人真正需要該服務的時候。

下面我們將使用ServiceFactory更新我們上面開發的com.Javaworld.sample.HelloServiceBundle,具體步驟如下:

1) 新建工廠 類HelloServiceFactory.Java,源代碼如清單9所示。

源代碼清單9 . HelloServiceFactory.Java

  1. public class HelloServiceFactory implements ServiceFactory{
  2. private int usageCounter = 0;
  3. public Object getService(Bundle bundle,ServiceRegistration registration) {
  4. System.out.println("Create objectof HelloService for " + bundle.getSymbolicName());
  5. usageCounter++;
  6. System.out.println("Number ofbundles using service " + usageCounter);
  7. HelloService helloService = newHelloServiceImpl();
  8. return helloService;
  9. }
  10. public void ungetService(Bundle bundle,ServiceRegistration registration, Object service) {
  11. System.out.println("Release objectof HelloService for " + bundle.getSymbolicName());
  12. usageCounter--;
  13. System.out.println("Number ofbundles using service " + usageCounter);
  14. }
  15. }

從上面的代碼中,我們可以看到,ServiceFactory接口定義了兩個方法:

a) getService()方法:當某個Bundle第一次使用BundleContext.getService(ServiceReference)方法請求一個服務對象時,OSGi框架會調用該方法。在源代碼清單9中,我們用這個方法為每個Bundle新建並返回不同的HelloServiceImpl對象,如果這個對象不是null,OSGi框架會緩存這個對象。如果同一個Bundle再次調用BundleContext.getService(ServiceReference)方法,OSGi將返回同一個服務對象。

b) ungetService()方法:當Bundle釋放服務時,OSGi容器可以調用該方法銷毀服務對象。在源代碼清單9中,我們使用usageCounter變量來跟蹤服務的使用數目,並打印出該服務的客戶端數量。

2) 修改HelloService Bundle中的HelloServiceActivator.Java的start()方法,讓它注冊到ServiceFactory接口名下,而不是注冊到HelloService接口。詳細代碼如清單10所示:

源代碼清單10. 修改後的HelloServiceBundle中的HelloServiceActivator.Java

  1. package com.Javaworld.sample.service.impl;
  2. importorg.osgi.framework.BundleActivator;
  3. importorg.osgi.framework.BundleContext;
  4. importorg.osgi.framework.ServiceRegistration;
  5. importcom.Javaworld.sample.helloservice.HelloServiceFactory;
  6. importcom.Javaworld.sample.service.HelloService;
  7. publicclass HelloServiceActivator implements BundleActivator {
  8. ServiceRegistrationhelloServiceRegistration;
  9. public void start(BundleContext context)throws Exception {
  10. HelloServiceFactory helloServiceFactory= new HelloServiceFactory();
  11. helloServiceRegistration=context.registerService(HelloService.class.getName(), helloServiceFactory,null);
  12. }
  13. public void stop(BundleContext context)throws Exception {
  14. helloServiceRegistration.unregister();
  15. }
  16. }

現在,您可以試運行示例代碼。您會注意到,當HelloWorld Bundle啟動時,服務計數器變為1;當HelloWorldBundle停止時,服務計數器的數目將變為0。

5.4. 跟蹤服務

在“OSGi服務”小節,您學會了如何使用服務的接口名搜索服務。但如果有多個Bundle使用同一接口名注冊服務,那會發生什麼呢?這時,OSGi容器將返回排行最高的服務,即,返回注冊時那個SERVICE_RANKING屬性值最大的服務。如果有多個服務的排行值相等,那麼OSGi容器將返回PID值最小的那個服務。

但是,如果您的服務消費者需要了解某一接口下的服務對象何時注冊、何時取消注冊,這時,您應使用ServiceTracker類。下面,我們看看如何使用服務跟蹤器來修改我們的示例代碼,具體步驟如下。

1) 修改HelloWorldBundle的MANIFEST.MF文件,讓它導入org.osgi.util.tracker包;

2) 新建類HelloServiceTracker.Java,其源代碼參見清單11。

源代碼清單11.HelloServiceTracker.Java

  1. public class HelloServiceTracker extends ServiceTracker {
  2. public HelloServiceTracker(BundleContext context) {
  3. super(context, HelloService.class.getName(),null);
  4. }
  5. public Object addingService(ServiceReference reference) {
  6. System.out.println("Inside HelloServiceTracker.addingService " + reference.getBundle());
  7. return super.addingService(reference);
  8. }
  9. public void removedService(ServiceReference reference, Object service) {
  10. System.out.println("Inside HelloServiceTracker.removedService " + reference.getBundle());
  11. super.removedService(reference, service);
  12. }
  13. }

在上面的HelloSerivceTracker類的構造函數中,您可以看到,我們把HelloService接口名傳入其父類中,這相當於說,HelloServiceTracker應跟蹤注冊到HelloService接口名下的所有服務,HelloServiceTracker繼承自ServiceTracker類,實現了下面兩個方法:

a) addingService()方法:當Bundle使用接口名注冊服務時,該方法將會被調用;

b)removedService()方法:當Bundle取消注冊某個接口名下的服務時,該方法將會被調用。

3) 用HelloServiceTracker類更新我們的Activator.Java類,以便讓它來管理服務,而不是直接去查找它們,源代碼請參見清單12。

源代碼清單12. 使用了HelloServiceTracker的Activator.Java

  1. public class Activator implements BundleActivator {
  2. HelloServiceTracker helloServiceTracker;
  3. public void start(BundleContext context) throws Exception {
  4. System.out.println("Hello World!!");
  5. helloServiceTracker= new HelloServiceTracker(context);
  6. helloServiceTracker.open();
  7. HelloService helloService = (HelloService)helloServiceTracker.getService();
  8. System.out.println(helloService.sayHello());
  9. }
  10. public void stop(BundleContext context) throws Exception {
  11. System.out.println("Goodbye World!!");
  12. helloServiceTracker.close();
  13. }
  14. }

我們看到,在初始的start()方法中,我們首先新建一個HelloServiceTracker對象,然後要求這個對象跟蹤HelloService接口下的服務。這時,我們可以調用getService()方法獲得HelloService對象。

如果您試運行上面的示例代碼,您會注意到,在啟動或停止HelloSerivceBundle時,OSGi容器都會調用HelloServiceTracker對象的addingService()方法或removedService()方法。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved