领先一步
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 绝对值得您进一步关注。