保持领先
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 "http://localhost: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。