保持領先
VMware 提供培訓和認證,助您快速進步。
瞭解更多一段時間以來,我一直對以客戶端為中心、基於 Web 的使用者介面很感興趣。這些第四代框架的特點是其基於元件、事件驅動的程式設計模型,並且專注於完全駐留在客戶端的表示層邏輯。以這種方式面向 Web 瀏覽器通常需要使用 JavaScript 或 Flash,這本身帶來了一些獨特的挑戰。
如果我們能用 Java 程式設計並自動生成 JavaScript 或 Flash 執行時模組,就可以解決其中許多挑戰。目前實現此目標的兩個知名產品分別是 Google Web Toolkit (GWT) 和 Open Laszlo。兩者都可在 OSI 批准的許可證下獲得,並擁有活躍的社群,但各自具有獨特的複雜性。一個需要考慮的問題是,它們在多大程度上實現了提供一個透明的、針對 Web 瀏覽器部署的基於 Java 的開發環境的目標。這個考量有多個方面,包括 IDE 支援、除錯整合、反射能力、執行時元件繫結等等。所有這些都是使用傳統的 Java 技術(例如 Swing 和 Standard Widget Toolkit (SWT))開發富客戶端時需要考慮的常規事項。
這篇博文的目的不是為了批評 GWT 或 Open Laszlo。相反,我想探討一個名為 Java2Script Pacemaker (J2S) 的開源 Java 到 JavaScript 編譯器,並介紹最初的 Spring 整合。這個有趣的專案並不廣為人知,但它以令人鼓舞的方式解決了透明的 Java 到 JavaScript 開發問題。J2S 附帶一個增量編譯器,幾乎完整的 java.lang、java.io 和 java.util 包的 JavaScript 版本,JUnit 的 JavaScript 版本,一個 Eclipse SWT JavaScript 實現,以及一個 AJAX 庫。更重要的是,J2S 可以將任何現有的 Java 程式碼轉換為 JavaScript,前提是原始碼和依賴項也同樣轉換為 JavaScript。
從技術角度考慮,J2S 目前在幾個主要方面與 GWT 不同。首先是其編譯器技術,它基於 Eclipse 抽象語法樹 (AST),因此需要 Eclipse。然而,Eclipse JDT Core 支援無頭模式,因此從 Ant 外掛或 Maven mojo 執行 J2S 編譯並不困難。第二個區別是 J2S 提供了全面的執行時反射和元件繫結能力。GWT 更偏愛編譯時 JavaScript 最佳化,但這犧牲了這些執行時服務。另一方面,J2S 認識到,結合 摩爾定律、改進的瀏覽器 JavaScript 直譯器和類似於 JNI 的 JavaScript 最佳化,共同為實現足夠的效能提供了空間,同時仍能享受更完整的 JRE 模擬及其他執行時服務。
也許最大的技術差異在於使用者介面方法。GWT 提供了自己的類似 Swing 的 API,該 API 專為 Web 瀏覽器整合而設計。另一方面,J2S 旨在提供 SWT 的實現。J2S 的方法有許多明顯的優勢
透過生成基於 JavaScript 的客戶端,J2S 顯然需要某種形式的 Java 到 JavaScript 遠端呼叫。Java 到 JavaScript 遠端呼叫實現通常採用非同步方法,這意味著遠端呼叫後會立即繼續執行,並且在接收到結果時會有一個單獨的回撥來處理呼叫結果。Java 到 JavaScript 遠端呼叫的兩種主要方法是 DWR 和 JSON-RPC,儘管 GWT 和 J2S 都提供了自己的獨立遠端呼叫方法。GWT 和 J2S 的方法均不提供開箱即用的 Spring 整合,但 Spring 靈活的架構使其很容易實現(我將在下面展示 J2S 的情況)。
在檢視 Spring 實現之前,我們先回顧一下 J2S AJAX 遠端呼叫協議是如何工作的。J2S 對每個潛在的遠端呼叫採用準 命令模式。SimpleRPCRunnable 超類提供了 JavaScript 到 Java 和 Java 到 JavaScript 的序列化,子類指示遠端 URL、要序列化的欄位和要在遠端執行的邏輯
public class LZ77JSSimpleRPCRunnable extends SimpleRPCRunnable {
private transient SomeServicesLayer servicesLayer; // setter omitted
public String jsContent;
public String result;
public String getHttpURL() {
return "https://:8080/echotest/simplerpc";
}
public void ajaxRun() {
result = servicesLayer.computeTheAnswer(jsContent);
jsContent = null;
}
}
欄位宣告很重要。每個公共的非 transient 欄位都將被 SimpleRPCRunnable 序列化。getHttpURL() 指定了 J2S Servlet 的 URL。相同的 URL 可用於任何 J2S 命令,使其成為您應用程式的 J2S 前端控制器。ajaxRun() 方法包含將在伺服器端執行的邏輯。在本例中,我們的 ajaxRun() 方法正在訪問一個本地(伺服器端)Spring bean。請注意 servicesLayer 欄位被宣告為 transient,這意味著 SimpleRPCRunnable 不會對其進行序列化。相反,Spring IoC 容器會將 SomeServicesLayer 例項依賴注入到我們的伺服器端命令物件中。因此,servicesLayer 在 J2S 客戶端始終為 null。客戶端非同步呼叫命令時,會使用類似以下的程式碼:
SimpleRPCSWTRequest.swtRequest(new LZ77JSSimpleRPCRunnable() {
public void ajaxIn() {
jsContent = sourceText.getText();
}
public void ajaxOut() {
resultText.setText(result);
}
});
如上所示,ajaxIn() 方法用於在客戶端設定公共欄位為可接受的值。ajaxOut() 方法是非同步回撥處理器,這意味著一旦命令物件從伺服器返回並反序列化後就會執行它。在這種情況下,該命令正在更新一個 UI 元件。下面的螢幕截圖顯示了作為 JVM 託管的 SWT 應用程式執行該命令的結果
在伺服器端,我們沒有使用普通的 J2S SimpleRPCHttpServlet。相反,我們使用了一個名為 SpringRpcHttpServlet 的新類(該類以 ZIP 附件 的形式提供,與本文中引用的其他程式碼一起)。SpringRpcHttpServlet 的操作與普通 SimpleRPCHttpServlet 相同,不同之處在於它從 Spring 應用程式上下文中獲取伺服器端命令物件。程式碼有良好的文件,如果您有興趣詳細瞭解其工作原理,請檢視 ZIP 附件。本質上,它允許您在 Spring 應用程式上下文檔案中定義命令及其依賴項。
如果您的應用程式需要額外的命令,只需建立一個 SimpleRPCRunnable 子類,然後將其新增到您的應用程式上下文中即可。關注我在 ROO 上的工作的人可能會有興趣瞭解到,我打算提供 J2S 遠端呼叫整合,這將使您無需編寫命令物件或透過 SimpleRPCSWTRequest 進行呼叫。
總之,J2S 對於需要 JavaScript 編譯或 SWT Web 瀏覽器實現的的專案來說,帶來了一些誘人的優勢。它還可以與 Spring 後端成功互操作。J2S 有意選擇利用 AST 和 SWT 等成熟的現有技術,這使其成為重用現有程式碼和開發者技能的典範,從而降低了採用門檻和實質性 API 變更的可能性。如果您認為自己是早期採用者、SWT 的熱衷者,或者需要一個基於成熟 SWT UI 框架構建的以客戶端為中心、基於 Web 的使用者介面,那麼絕對值得仔細看看 J2S。