領先一步
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;
}
}
欄位宣告很重要。每個公共的非瞬態欄位都會被 `SimpleRPCRunnable` 序列化。`getHttpURL()` 指定了 J2S servlet 的 URL。任何 J2S 命令都可以使用相同的 URL,使其成為您應用程式的 J2S 前端控制器。`ajaxRun()` 方法包含將在伺服器端執行的邏輯。在本例中,我們的 `ajaxRun()` 方法正在訪問本地(伺服器端)的 Spring bean。注意 `servicesLayer` 欄位宣告為瞬態,這意味著 `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 的擁護者,或者需要一個以客戶端為中心、基於 Web 的使用者介面,並且構建在成熟的 SWT UI 框架之上,那麼 J2S 絕對值得您進一步關注。