使用AJAX和Spring集成进行Java到JavaScript的编译

工程 | 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版本,一个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的方法有一些明显的优势:

  • 您的应用程序可以同时定位富客户端(在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://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应用程序执行的结果。

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调用。

总之,对于需要 JavaScript 编译或 Web 浏览器实现 SWT 的项目而言,J2S 提供了一些诱人的优势。它还可以与 Spring 后端成功互操作。J2S 有意选择利用经过验证的现有技术,如 AST 和 SWT,这使其成为重用现有代码和开发人员技能的良好示例,从而降低了采用门槛和重大 API 变更的可能性。如果您认为自己是早期采用者、SWT 爱好者,或者需要一个基于成熟 SWT UI 框架的以客户端为中心的 Web 用户界面,那么绝对值得仔细研究 J2S。

获取 Spring 新闻通讯

关注 Spring 新闻通讯

订阅

领先一步

VMware 提供培训和认证,以加速您的进步。

了解更多

获取支持

Tanzu Spring 在一个简单的订阅中提供 OpenJDK™、Spring 和 Apache Tomcat® 的支持和二进制文件。

了解更多

即将举行的活动

查看 Spring 社区中所有即将举行的活动。

查看全部