本文為《你好,OSGi》系列第四部分,深入講述 OSGi 服務機制。OSGi 架構極適合實現面向服務應用 (SOA):Bundle 可導出服務,其他 Bundle 無需知曉來源 Bundle 任何資訊即可消費服務。OSGi 隱藏真實服務實作類的能力,為 SOA 提供優秀的類與介面組合。
OSGi 框架中,來源 Bundle 在容器註冊 POJO 物件(無需實作介面或繼承超類),可在一或多個介面名下註冊對外提供服務。目標 Bundle 向容器請求特定介面下的服務,發現後綁定至介面並調用方法。
更新 HelloService Bundle 導出 HelloServiceImpl 物件:
MANIFEST.MF 導入 org.osgi.framework 套件HelloServiceActivator.java(代碼清單 7)public class HelloServiceActivator implements BundleActivator {
ServiceRegistration helloServiceRegistration;
public void start(BundleContext context) throws Exception {
HelloService helloService = new HelloServiceImpl();
helloServiceRegistration = context.registerService(
HelloService.class.getName(), helloService, null);
}
public void stop(BundleContext context) throws Exception {
helloServiceRegistration.unregister();
}
}
registerService() 三參數:介面名稱(或字串陣列多介面)、服務實體物件、服務屬性字典(供消費者過濾)。
MANIFEST.MF 設定 Bundle-Activator: com.javaworld.sample.service.impl.HelloServiceActivator容器啟動 Bundle 時將控制權交給 Activator,註冊服務至登記處。
修改 HelloWorld Bundle 成為消費者,更新 Activator.java(代碼清單 8):
public class Activator implements BundleActivator {
ServiceReference helloServiceReference;
public void start(BundleContext context) throws Exception {
System.out.println("HelloWorld!!");
helloServiceReference = context.getServiceReference(HelloService.class.getName());
HelloService helloService = (HelloService) context.getService(helloServiceReference);
System.out.println(helloService.sayHello());
}
public void stop(BundleContext context) throws Exception {
System.out.println("Goodbye World!!");
context.ungetService(helloServiceReference);
}
}
getServiceReference() 返回註冊於介面下的服務引用(多服務時返回最高排名者)。取得引用後調用 getService() 獲取真實服務物件。
預設單一服務物件共享。若需:
• 每消費者 Bundle 返回不同服務實例
• 服務物件昂貴(如資料庫連線)採延遲建立
解法:實作 ServiceFactory 介面註冊為服務(代碼清單 9)。
public class HelloServiceFactory implements ServiceFactory {
private int usageCounter = 0;
public Object getService(Bundle bundle, ServiceRegistration registration) {
System.out.println("Create object of HelloService for " + bundle.getSymbolicName());
usageCounter++;
System.out.println("Number of bundles using service " + usageCounter);
return new HelloServiceImpl();
}
public void ungetService(Bundle bundle, ServiceRegistration registration, Object service) {
System.out.println("Release object of HelloService for " + bundle.getSymbolicName());
usageCounter--;
System.out.println("Number of bundles using service " + usageCounter);
}
}
修改 HelloServiceActivator 註冊工廠而非實體(代碼清單 10)。執行時服務計數器隨 Bundle 啟動/停止增減。
多 Bundle 註冊同介面服務時,容器返回最高排名服務。若消費者需知悉服務註冊/註銷時機,使用 ServiceTracker(代碼清單 11-12)。
public class HelloServiceTracker extends ServiceTracker { public HelloServiceTracker(BundleContext context) { super(context, HelloService.class.getName(), null); } public Object addingService(ServiceReference reference) { System.out.println("Inside HelloServiceTracker.addingService " + reference.getBundle()); return super.addingService(reference); } public void removedService(ServiceReference reference, Object service) { System.out.println("Inside HelloServiceTracker.removedService " + reference.getBundle()); super.removedService(reference, service); } }更新
Activator使用 Tracker 管理服務(代碼清單 12)。啟動/停止 HelloService Bundle 時,容器將調用 Tracker 的addingService()/removedService()。