Java 到 JavaScript 編譯,結合 AJAX 和 Spring 整合

技術 | Ben Alex | 2007 年 1 月 22 日 | ...

一段時間以來,我一直對以客戶端為中心、基於 Web 的使用者介面很感興趣。這些第四代框架的特點是其基於元件、事件驅動的程式設計模型,並且專注於完全駐留在客戶端的表示層邏輯。以這種方式面向 Web 瀏覽器通常需要使用 JavaScriptFlash,這本身帶來了一些獨特的挑戰。

如果我們能用 Java 程式設計並自動生成 JavaScript 或 Flash 執行時模組,就可以解決其中許多挑戰。目前實現此目標的兩個知名產品分別是 Google Web Toolkit (GWT) 和 Open Laszlo。兩者都可在 OSI 批准的許可證下獲得,並擁有活躍的社群,但各自具有獨特的複雜性。一個需要考慮的問題是,它們在多大程度上實現了提供一個透明的、針對 Web 瀏覽器部署的基於 Java 的開發環境的目標。這個考量有多個方面,包括 IDE 支援、除錯整合、反射能力、執行時元件繫結等等。所有這些都是使用傳統的 Java 技術(例如 SwingStandard 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 的方法有許多明顯的優勢

  • 您的應用程式可以同時面向富客戶端(在 JVM 中)和 Web UI(在 Web 瀏覽器中),幾乎無需或僅需少量重寫程式碼;
  • 開發和除錯 J2S 應用程式與開發和除錯普通 SWT 應用程式基本相同,這大大降低了新開發人員的學習曲線;
  • 組織有很大機會找到具備 SWT 技能的人員;
  • 有大量的 文獻社群 資源 可用於 SWT;
  • SWT 是一個穩定且經過生產環境驗證的 API;並且
  • 同時有 開源商業 工具可用於幫助您構建 SWT 應用程式。
然而,J2S 確實有一些需要考慮的限制。其中包括
  • 網際網路上的下載速度可能較慢。考慮到大多數 JEE 應用程式用於內網部署,我不確定這是否會構成主要障礙。此外,JavaScript 壓縮和客戶端快取可以最大程度地減少這些延遲。
  • 執行速度可能較慢,儘管我發現在程式碼下載後並不是太糟糕。有多種可用的最佳化方法來提高速度。對於高階使用者,還可以選擇下載 JVM 託管的 SWT 版本使用者介面,由於共享 SWT 程式碼庫,這帶來的額外開發成本應該有限。
  • JFace 目前不支援,並且沒有模擬完整的 JRE。一個值得注意的例外是 java.io.File。如果您依賴此類別,您的應用程式將無法本地編譯為 JavaScript。相反,您需要使用 J2S 的 @j2sNative 能力將相關類編譯為 JavaScript。我還在 SWT 實現中遇到了一些小問題,但都不太嚴重。
  • J2S 社群 相對較小,並且該框架迄今為止尚未得到廣泛使用。儘管如此,每個開源專案都是從小開始的,需要有機會成長。
  • 非 Eclipse IDE 使用者將無法以目前的形式使用 J2S。如前所述,AST 編譯模型允許 Eclipse JDT 的無頭支援,因此這不是一個主要問題。
Spring 社群也很想知道,“它與 Spring 一起工作嗎?”。答案實際上取決於您想要實現什麼。如果您的目標是構建標準的 SWT 或 Swing 應用程式,通常會在使用者介面層使用這些技術來訪問遠端服務層。因此,您的主要 Spring 整合問題涉及到是否有一個合適的遠端呼叫機制。Spring 為 Java 到 Java 的遠端呼叫提供了多種成熟的 基礎設施 選擇,大多數專案選擇同步的 HttpInvokerRMISOAP

透過生成基於 JavaScript 的客戶端,J2S 顯然需要某種形式的 Java 到 JavaScript 遠端呼叫。Java 到 JavaScript 遠端呼叫實現通常採用非同步方法,這意味著遠端呼叫後會立即繼續執行,並且在接收到結果時會有一個單獨的回撥來處理呼叫結果。Java 到 JavaScript 遠端呼叫的兩種主要方法是 DWRJSON-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 應用程式執行該命令的結果

Figure 1
下一個螢幕截圖顯示了作為 Firefox 託管的 SWT 應用程式執行相同命令的結果。在這些執行時目標之間切換無需更改程式碼或遠端呼叫配置,這展示了 J2S 方法的靈活性和吸引力
Figure 2
SimpleRPCSWTRequest 還提供了兩個靜態方法,用於宣告呼叫是否實際在網路上傳輸。SimpleRPCSWTRequest.switchToLocalJavaThreadMode() 將導致 ajaxRun() 方法在本地被呼叫,如果您正在執行 JVM 託管的 SWT 應用程式,這可能是合適的。要使呼叫在網路上傳輸(從而使 ajaxRun() 在伺服器上執行),只需呼叫 SimpleRPCSWTRequest.switchToAJAXMode()。此模式與瀏覽器和 JVM 目標平臺都相容,因此使用 J2S 構建多目標使用者介面無需為 JVM 目標使用額外的遠端呼叫協議(例如 HttpInvoker 或 RMI)。

在伺服器端,我們沒有使用普通的 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。

訂閱 Spring 郵件列表

訂閱 Spring 郵件列表,保持聯絡

訂閱

保持領先

VMware 提供培訓和認證,助您快速進步。

瞭解更多

獲取支援

透過一個簡單的訂閱,Tanzu Spring 為 OpenJDK™、Spring 和 Apache Tomcat® 提供支援和二進位制檔案。

瞭解更多

即將舉行的活動

檢視 Spring 社群所有即將舉行的活動。

檢視全部