领先一步
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版本,一个JavaScript版本的Junit,一个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提供了它自己专为Web浏览器集成设计的类似Swing的API。另一方面,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://127.0.0.1:8080/echotest/simplerpc";
}
public void ajaxRun() {
result = servicesLayer.computeTheAnswer(jsContent);
jsContent = null;
}
}
字段声明很重要。每个公共的非瞬态字段都将由SimpleRPCRunnable序列化。getHttpURL()指定J2S servlet的URL。相同的URL可用于任何J2S命令,使其成为应用程序的J2S前端控制器。ajaxRun()方法包含将在服务器端执行的逻辑。在这种情况下,我们的ajaxRun()方法正在访问本地(服务器端)Spring bean。请注意,servicesLayer字段声明为瞬态的,这意味着SimpleRPCRunnable不会序列化它。相反,Spring IoC容器将依赖注入SomeServicesLayer实例到我们的服务器端命令对象中。因此,在J2S客户端,servicesLayer始终为null。对于客户端异步调用命令,他们将使用如下代码:
SimpleRPCSWTRequest.swtRequest(new LZ77JSSimpleRPCRunnable() {
public void ajaxIn() {
jsContent = sourceText.getText();
}
public void ajaxOut() {
resultText.setText(result);
}
});
如图所示,ajaxIn()方法用于在客户端将公共字段设置为可接受的值。ajaxOut()方法是异步回调处理程序,这意味着一旦命令对象从服务器返回并反序列化,它就会被执行。在这种情况下,该命令正在更新UI组件。下面的屏幕截图显示了将命令作为JVM托管的SWT应用程序执行的结果。
下一个屏幕截图显示了将相同的命令作为Firefox托管的SWT应用程序执行的结果。在这些运行时目标之间不需要更改代码或远程配置,这说明了J2S方法的灵活性和吸引力。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调用。
总之,对于需要 JavaScript 编译或 Web 浏览器实现 SWT 的项目而言,J2S 提供了一些诱人的优势。它还可以与 Spring 后端成功互操作。J2S 有意选择利用经过验证的现有技术,如 AST 和 SWT,这使其成为重用现有代码和开发人员技能的良好示例,从而降低了采用门槛和重大 API 变更的可能性。如果您认为自己是早期采用者、SWT 爱好者,或者需要一个基于成熟 SWT UI 框架的以客户端为中心的 Web 用户界面,那么绝对值得仔细研究 J2S。