在大多數情況下,Java 應用程序要麼是 J2EE 應用程序、要麼是 J2SE 應用 程序,並且在這一點上是泾渭分明的。J2EE 應用程序需要 J2EE 容器的服務, 容器要實現一長串的 J2EE API,包括 Enterprise JavaBean (EJB)、JTA、JNDI 、JMS、JCA 和 JMX。J2EE API 設計為協同工作;畢竟,J2EE 設計是從多年來 數百人開發企業應用程序的經驗中提取出的公共需求。像所有框架一樣,J2EE API 的主要目的是“不重新發明輪子”。
有一些 API 屬於 J2EE 規范的一部分,但是可以很容易地在 J2SE 應用程序 中使用,如 JDBC、JSP 和 servlet,但是對於大多數 J2EE API,J2EE 是一個 要麼是要麼不是的命題--大多數 J2EE API 需要全功能的 J2EE 容器。不過,有 一些服務器應用程序開發為 J2SE 應用程序而非 J2EE 應用程序,這通常都有很 好的理由。為什麼這些應用程序的開發人員必須重新發明輪子呢?J2EE 中是否 有部分內容可以容易地被 J2SE 應用程序借用來提供同樣的優點呢?什麼組件可 以同時用於 J2EE 和 J2SE 應用程序的組件呢?
松散耦合
J2EE 的一個主要設計原理是 J2EE 應用程序可以松散地耦合--用組件組裝, 在組裝或者部署應用程序時而不是在組件開發時定義或者改變這些組件的相互連 接。J2EE 組件使用 JNDI 相互查找和查找所需要的資源,如 JDBC 和 JMS 連接 。JMS 這樣的技術鼓勵松散耦合,允許靈活地為工作流程建模、容易分配處理任 務、可伸縮性和容錯性。很多 J2SE 服務器應用程序也可以從這些技術和原理中 受益。
像 JDBC、JMS 和 JNDI 這樣的 API 基本上是“中間件”--它們作為應用程 序與不同的服務提供者之間的統一接口。幾乎每一個數據庫服務器都有 JDBC 驅 動程序,有大量的免費數據庫服務器,所以幾乎每一個希望利用數據庫的 Java 應用程序都可以容易地做到這一點。不過,對於 JMS 就不是這樣了。消息隊列 服務器遠沒有數據庫這樣常見,特別是在小型商店中。但是有大量的應用程序可 以通過使用消息隊列而極大地受益。
Somnifugi JMS
消息隊列是一個功能強大的范例,它用於構建健壯的、靈活的、松散耦合的 、可伸縮的應用程序。有一些商業消息隊列產品,如 WebSphere MQ、Sonic、 Fiorano、JBossMQ 和 SpiritWave。就像 JDBC 對於數據庫一樣,JMS 是消息的 中間件--它使得應用程序可以通過統一的接口訪問不同的消息隊列產品,這個接 口提供了像 Connection 、 Topic 和 Message 這樣的抽象。
許多 J2SE 應用程序使用某種形式的消息隊列,但是不使用 JMS--而是使用 線程池、工作隊列、任務管理器等。AWT 和 Swing 框架使用消息(事件)在模 型與視圖層之間通信,JavaBean 組件利用監聽器支持一種基於主題的消息。消 息隊列提供了很多結構上的優點--它固有的松散耦合有利於采用靈活的、基於組 件的方法,並提供了有助於簡化設計和減少互連的天然抽象邊界。一個附帶的好 處是,MQ 范例使得分布式的、可伸縮的和容錯的設計變得更容易了,因為消息 生產者和使用者不一定需要運行在同一 JVM 中,大多數生產者/使用者任務的本 性是允許並發處理的。
J2SE 服務器應用程序的開發人員經常開發他們自己的消息層,或者從零開始 ,或者以 util.concurrent 這樣的庫為基礎構建。通常這麼做的理由是:
MQ 服務器是昂貴的和重量級的,並且由於我們不需要像持久性、分布式、事 務和驗證這樣更重量級的功能,構建自己內存中的消息層,只提供所需要的功能 會更容易。
雖然這在一般情況下是正確的,但是應用程序需求通常會隨著時間而增加, 在開始時不需要的一些消息功能在以後可能會變得更重要了。
面向消息的應用程序的開發人員在開發過程的初期就必須做出選擇--選擇一 個商業消息產品,或者構建自己的更便宜的、更輕量級的解決方案。Somnifugi JMS 包結合了這兩種方式--一個基於高性能的 util.concurrent 庫的非持久內 存中消息隊列服務,和一個符合 JMS API 的接口。與傳統 JMS 提供者相比較, Somnifugi 是相當輕量級的,不管是功能上還是性能上。它只限於在一個 JVM 中使用(盡管可以取消這種限制),並缺少持久性、事務和驗證功能。另一方面 ,它特別快--它比傳統 JMS 實現快得多,以至於可以在因性能原因可能無法使 用消息的地方使用它。為了表明 Somnifugi 到底有多輕量級,在它的分發中包 含了幾個用 JMS 主題取代 Swing/JavaBean 事件框架的例子。
增加的靈活性
Somnifugi 還提供了另一項重要的優點:現在可以開發使用 JMS 接口的組件 ,然後在部署應用程序時決定是使用更快的、內存中的 Somnifugi 提供者還是 更重量級的、但是更可靠的提供者,如 WebSphere MQ。可以將這種選擇推遲到 部署時的好處非常巨大--特別是因為需求可能會在項目的開發過程中變化時--並 提供了代碼重用的機會,對於自已開發的消息層來說這是不太可能做到的。
在 J2SE 中使用 JNDI
像 JDBC 和 JMS 一樣,JNDI 是一種中間件。像 JMS 一樣,在 J2SE 應用程 序中使用 JNDI 不像使用 JDBC 那麼容易。JDBC 提供者無處不在--有數十種兼 容 JDBC 的商業和開放源代碼數據庫服務器。雖然所有 J2EE 容器都必須包括一 個 JNDI 提供者,但是對於不屬於 J2EE 容器的 JNDI,數量相對就少了。這不 僅使在 J2SE 應用程序中使用 JNDI 更困難了,而且還意味著 J2SE 開發人員不 太可能接觸到 JNDI 並認識它的優勢。
根據所使用的 JNDI 提供者和應用程序配置,JNDI 可能在 JNDI 名稱空間中 存儲任意的 Java 對象(有一些限制:有些 JNDI 提供者限制存儲的對象是可序 列化的)。一般用 JNDI 存儲靜態配置數據(整型和字符串型)、JDBC DataSource 對象、JMS Connection 和 Topic 對象,以及無狀態的對象(包括 工廠對象)。完整地存儲已配置對象,比如 JDBC DataSource 對象,而不只是 配置數據,比如 JDBC URL,還可以增強應用程序的安全性,因為像授權憑證這 樣的敏感信息不能被應用程序直接使用。
J2EE 應用程序使用 JNDI 作為連接松散耦合組件之間的“開關板”--J2EE 組件使用 JNDI 尋找其他想要使用的組件,如 EJB 組件,並尋找 JDBC 和 JMS 連接這樣的資源。J2EE 組件之間的互連是在組件的部署描述符中聲明式地定義 的,容器自動將對象綁定到名稱空間中的特定位置,並保證在部署組件之前組件 之間的所有資源依賴關系都得到滿足。
J2SE 應用程序可以以類似於 J2EE 應用程序的方式使用 JNDI,只是它們必 須多做一些填充名稱空間的工作。但是好處是相同的--應用程序可以更松散地耦 合,組件在運行時彼此發現。
免費 JNDI 提供者
雖然 JNDI 參考實現不包括一般性的 JDNI 提供者,但是可以下載 Sun 網站 提供的 File System (FSContext)。這是一個示例 JNDI 提供者,它是以源代 碼的方式提供的,它訪問並存儲文件中的可序列化對象,還使名稱空間的內容可 以保證跨程序調用的一致性。雖然 FSContext JNDI 提供者主要是做為編寫 JNDI 提供者的一個示例,但是簡單的應用程序也可以使用它作為序列化對象的 持久性數據存儲,或者是作為“存根” JNDI 提供者,對從 JNDI 獲得其配置的 組件進行單元測試。
JBoss 開放源代碼 J2EE 容器還包括一個更一般性的 JNDI 提供者 JNPServer,它可以容易地作為單獨的 JNDI 提供者運行,不需要 JBoss 容器。 可以通過 RMI 從遠程 JVM 訪問 JNP,而在本地 JVM 中不會產生 RMI 開銷。它 在內部將對象存儲到內存中的一個 HashMap 中。
在 JBoss 發行版的 jnpserver.jarJAR 文件中可以找到 JNP JNDI 服務器, 它還依賴於 log4j 日志引擎。要使用它,必須配置 log4j,創建相應的 jndi.properties文件(參見清單 1),並安排通過調用同一 JVM 或者另一個 JVM 中的 org.jnp.server.Main 的主入口點來啟動服務器。訪問 JNDI 名稱空 間的類文件在 JBoss 發行版的 jnpclient.jarJAR 文件中。
清單 1. JNPServer 的 jndi.properties
java.naming.factory.initial=org.jnp.interfaces.NamingContextF actory
java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces
# Uncomment this line only if the JNDI server is to run in another JVM;
# otherwise, local JNDI requests will go over RMI
#java.naming.provider.url=localhost
Java 管理擴展(JMX)
Java 管理擴展(Java Management Extensions,JMX)是一種管理組件和服 務的生命周期的機制。JBoss 大量使用 JMX--JBoss 中的幾乎所有組件都作為 JMX 服務提供。結果就是很容易配置一個只包括所需服務的應用程序。對於每一 個組件服務,創建一個名為 MBean (托管的 bean)對象,它包含生命周期方法 ( start() 和 stop() )和公開屬性的 getter 和 setter。清單 2 顯示了描 述一個簡單 Web 容器服務的 MBean 接口:
清單 2. 簡單 Web 容器服務的 MBean 接口
public interface WebServerMBean {
// Lifecycle methods
void create() throws Exception;
void start() throws Exception;
void stop();
void destroy();
// Getter and setter for listener-port property
int getPort();
void setPort(int port);
// Get the names of loaded Web applications
String[] getWebApplications();
}
JBoss 還包括一個 Web 應用程序( jmx-console ),它可以查看當前裝載 到 JBoss 服務器中的 MBeans、檢查它們當前的狀態、並用浏覽器讀取和寫入它 們的屬性。(JMX 參考實現還包括一個名為 HtmlAdapter 的 Web 應用程序。)
雖然 JMX 是為 J2EE 提供的,但是也可以在 J2SE 應用程序中容易地使用它 。至少有兩個免費的 JMX 實現,Sun 的參考實現和開放源代碼 MX4J。編寫一個 MBean 以描述一個組件是相當簡單的--通常所要做的就是實現 start() 和 stop() 方法。編寫一個簡單的裝載一組 MBean 並啟動它們的 JMS “容器”只 需約 40 行代碼。遵循 JMX 標准,不但得到使用 JMS 的好處,比如遠程屬性檢 查和操縱(這對於調試及管理都有好處),而且還可以更容易地編寫可以輕易地 同時運行在 J2SE 和 J2EE 環境中的組件。
結束語
雖然 J2EE 和 J2SE 是用於不同工作的不同工具,但是許多開發人員發現自 己必須決定各種框架服務的“輕量級”和“重量級”實現,比如消息、配置或者 管理。通過使用 J2EE 接口的較輕量級的實現,如 Somnifugi JMS,開發人員可 以在短期內得到性能和易於使用的好處,同時又具有在將來需要時,可以容易地 遷移到更重量級的解決方案的靈活性。