「Hello OSGi」系列第三篇,手把手構建包含靜態 HTML 與 Servlet 的 OSGi Web Bundle,對比程式化註冊(Java 代碼調用 HttpService API)與聲明式註冊(plugin.xml 擴展點)兩種風格,幫助開發者根據專案需求選擇最適合的方式。
com.javaworld.sample.osgi.web.programmatic / ...declarativehelloworld.html(輸出 "Hello From helloworld.html")HelloWorldServlet 繼承 HttpServlet,doGet() 輸出 "Hello from HelloWorldServlet"Import-Package: javax.servlet;version="2.4.0",
javax.servlet.http;version="2.4.0",
org.osgi.framework;version="1.3.0",
org.osgi.service.http;version="1.2.0",
org.osgi.util.tracker;version="1.3.2"
public class Activator implements BundleActivator {
ServiceTracker httpServiceTracker;
public void start(BundleContext context) {
httpServiceTracker = new HttpServiceTracker(context);
httpServiceTracker.open();
}
public void stop(BundleContext context) {
httpServiceTracker.close();
}
}
public class HttpServiceTracker extends ServiceTracker {
public HttpServiceTracker(BundleContext context) {
super(context, HttpService.class.getName(), null);
}
public Object addingService(ServiceReference ref) {
HttpService httpService = (HttpService) context.getService(ref);
try {
// 靜態資源映射:URL → Bundle 內路徑
httpService.registerResources("/helloworld.html", "/helloworld.html", null);
// Servlet 映射:URL → Servlet 實例
httpService.registerServlet("/helloworld", new HelloWorldServlet(), null, null);
} catch (Exception e) { e.printStackTrace(); }
return httpService;
}
public void removedService(ServiceReference ref, Object svc) {
HttpService httpService = (HttpService) svc;
httpService.unregister("/helloworld.html");
httpService.unregister("/helloworld");
super.removedService(ref, svc);
}
}
關鍵點:
ServiceTracker 解決 HttpService 動態可用性:Bundle 啟動順序不固定,Tracker 確保服務就緒才註冊。registerResources(alias, path, HttpContext) 將 Bundle 內資源掛載至 HTTP 命名空間。registerServlet(alias, servlet, initParams, HttpContext) 掛載 Servlet,支援初始化參數。unregister,觸發 Servlet destroy() 生命週期。驗證:https://localhost/helloworld.html、https://localhost/helloworld
Import-Package: javax.servlet;version="2.4.0",
javax.servlet.http;version="2.4.0"
Require-Bundle: org.eclipse.equinox.http.registry
依賴 Equinox 專屬 http.registry Bundle,犧牲可移植性換取零代碼。
<?xml version="1.0" encoding="UTF-8"?>
<plugin>
<extension point="org.eclipse.equinox.http.registry.servlets">
<servlet alias="/decl/helloworld"
class="com.javaworld.sample.osgi.web.webapp.HelloWorldServlet"/>
</extension>
<extension point="org.eclipse.equinox.http.registry.resources">
<resource alias="/decl/helloworld.html" base-name="/helloworld.html"/>
</extension>
</plugin>
框架啟動時自動讀取 plugin.xml,向 HttpService 註冊對應資源與 Servlet。修改 URL 僅需改 XML,無需重新編譯 Java 代碼。
驗證:https://localhost/decl/helloworld.html、https://localhost/decl/helloworld
| 維度 | 程式化註冊 | 聲明式註冊 |
|---|---|---|
| 可移植性 | 標準 OSGi API,跨運行時 | 依賴 Equinox http.registry,鎖定平台 |
| 動態性 | 運行時可根據條件決定註冊邏輯 | 靜態 XML,部署後固定 |
| 維護性 | Java 代碼重構友好,IDE 支援完善 | XML 修改即時生效,適合運維調整 |
| 依賴管理 | 需手動追蹤 HttpService(ServiceTracker) | 框架自動管理生命週期 |
| 適用場景 | 需動態掛載、多租戶、條件註冊 | 固定端點、標準 Web 模組、追求零樣板 |
下一篇(系列最終篇)將詳細講解如何在命令列啟動 Equinox、安裝啟動 Bundle、嵌入 Jetty;以及如何打包 bridge.war 部署至 Tomcat,實現 OSGi 容器與傳統 Servlet 容器共生。