Web 2.0 开发与 Google Web Toolkit

作者:Federico Kereki

关于 Web 2.0 有很多炒作,大多数人认为像 Google 地图、Gmail 和 Flickr 这样的软件属于这一类。您是否想开发类似的程序,让用户可以拖动地图或刷新他们的电子邮件收件箱,而无需重新加载屏幕?

直到最近,创建这种高度交互式程序至少可以说是很困难的。开发工具少、调试帮助少以及浏览器不兼容性都加剧了复杂性。然而,现在,如果您想制作这种尖端的应用程序,您可以使用现代软件方法和工具,使用高级 Java 语言,并忘记 HTML、JavaScript 以及 Firefox 和 Internet Explorer 的行为是否相同。Google Web Toolkit (GWT) 使您可以轻松地做得更好,并为您的用户制作更现代的 Web 2.0 程序。

什么是 Web 2.0?

这个问题有几个答案,包括蒂姆·伯纳斯-李爵士(万维网的创建者)的观点,即这只是对已经存在的组件的重用。它最初是由蒂姆·奥莱利提出的,他提倡“Web 作为平台”,以数据作为驱动力,技术通过组装从分布式、不同、独立的开发人员和服务获取信息和功能的系统和站点来促进创新。

这个概念与让用户完全通过浏览器运行应用程序的想法一致,而无需在他们的机器上安装任何东西。这些新程序通常具有丰富的、用户友好的界面,类似于您从已安装程序中获得的界面,它们通常通过 AJAX(参见“什么是 AJAX?”侧边栏)来实现,以减少下载时间和加快显示时间。

Web 2.0 应用程序使用开发人员已经非常熟悉的相同基础设施:动态 HTML、CSS 和 JavaScript。此外,它们经常使用 XML 或 JSON 来表示和在服务器和浏览器之间通信数据。这种数据通信通常使用通过 DOM API XMLHttpRequest 的 Web 服务请求来完成。

什么是 AJAX?

Web 应用程序的标准模型是这样的:您从服务器获得一个屏幕的文本和字段,您填写一些字段,当您单击一个按钮时,浏览器将您键入的数据发送到服务器(等待),服务器处理它(等待),并发送回一个答案(等待),您的浏览器显示该答案,然后循环重新开始。这是迄今为止 Web 应用程序最常见的操作方式,您必须习惯这些延迟。没有任何事情是立即发生的,因为每个需要来自服务器的数据的答案都需要一个往返过程。

AJAX(异步 JavaScript 和 XML)是一种技术,它允许 Web 应用程序在后台(异步地)与 Web 服务器通信,以与其交换(发送或接收)数据。这消除了在每次操作或用户点击后重新加载整个页面的要求。因此,使用 AJAX 提高了交互水平,消除了等待页面重新加载的时间,并实现了增强的功能。一个良好编程的应用程序会在您做其他事情时在后台发送请求,因此您不必盯着空白屏幕或旋转的沙漏光标。这是 AJAX 首字母缩略词的异步部分。

AJAX 首字母缩略词的下一部分是 JavaScript。JavaScript 允许 Web 页面包含一个程序,这个程序允许 Web 页面如前所述连接到服务器。然而,这不仅仅是拥有 JavaScript 的问题,还取决于它在浏览器中是如何实现的。Firefox 和 Internet Explorer 都提供 AJAX 访问,但存在一些差异,因此程序员在进行连接时必须考虑到这些差异。数据通常使用 XMLHttpRequest 检索,但其他技术也是可能的,例如使用 iframes。

最后,AJAX 首字母缩略词的最后一部分是 XML。XML 是一种标准的标记语言,用于共享和传递信息。正如我们所见,用于发出 Web 服务请求的 DOM API 的名称是 XMLHttpRequest,最有可能的是,最初的意图是使用 XML 作为在浏览器和服务器之间交换数据的协议。然而,AJAX 中的 X 和 XMLHttpRequest 中的 XML 都不意味着您必须使用 XML;可以使用任何数据协议,包括没有协议。

JSON(JavaScript 对象表示法)经常被使用;它比 XML 更轻量级,正如您可能从其名称中猜到的那样,它通常更适合 JavaScript。参见图 3 中的一些实际 JSON 代码;请记住,它不是为了让人类清楚易懂,而是为了紧凑且易于机器理解。

AJAX 包含了一些存在已有一段时间的基本技术,AJAX 术语本身是在 2005 年由 Jesse Garrett 创建的。GWT 使用 AJAX 允许客户端程序以完全透明的方式与服务器通信或在其上执行过程。当然,您也可以显式地使用 AJAX 来实现您可能有的任何特殊目的。

什么是 Google Web Toolkit?

Google Web Toolkit(GWT——发音与“nitwit”押韵)是 Web 程序员的工具。它的首次公开亮相是在 2006 年 5 月的 JavaOne 会议上。目前(在撰写本文时),1.5.3 版本刚刚发布。它主要在 Apache 2.0 开源许可证下获得许可,但其某些组件在不同的许可证下获得许可。不要将 JavaScript 与 Java 混淆;尽管名称相似,但这两种语言是不相关的,相似之处来自一些共同的根源。

简而言之,GWT 使编写高性能、交互式 AJAX 应用程序变得更容易。您可以使用 Java 语言进行编码,而不是使用 JavaScript 语言(它功能强大,但在模块化和测试功能等领域有所欠缺,使得大规模系统的开发更加困难),GWT 会将 Java 语言编译成优化的、紧凑的 JavaScript 代码。此外,存在大量的软件工具来帮助您编写 Java 代码,您现在可以使用这些工具进行测试、重构、文档化和重用——所有这些都已成为 Web 应用程序的现实。

您也可以忘记 HTML 和 DHTML(动态 HTML,这意味着动态更改您在屏幕上看到的页面的实际源代码)以及其中的一些额外的细微兼容性问题。您可以使用 Java 小部件(例如文本字段、复选框等)进行编码,GWT 会负责将它们转换为基本的 HTML 字段和控件。也不用担心本地化问题;使用 GWT,可以轻松生成特定于语言环境的代码版本。

还有一个令人欢迎的额外好处。GWT 负责处理浏览器之间的差异,因此您不必花费时间以不同的方式编写相同的代码来满足每个浏览器的特殊怪癖。通常,如果您只是随意编码而不注意这些小细节,您的网站最终会在 Mozilla Firefox 中看起来不错,但在 Internet Explorer 或 Safari 中根本无法工作。这是一个众所周知的经典 Web 开发问题,在发布任何网站之前计划兼容性测试是明智之举。GWT 让您忘记这些问题,专注于任务本身。

根据其开发人员的说法,GWT 生成的高质量代码与手写 JavaScript 的质量(大小和速度)相匹配(甚至可能超过)。GWT 网页包含座右铭“比您手写的 AJAX 更快!”

GWT 还努力最大限度地减少生成的代码大小,以加快传输速度并缩短等待时间。默认情况下,最终代码大多是不可读的(面向浏览器,而不是窥探用户),但如果您有任何问题,您可以要求更易读的代码,以便您可以理解您的 Java 代码与生成的 JavaScript 之间的关系。

GWT 入门

在安装 GWT 之前,您应该在您的机器上安装一些东西

  • Java 开发工具包 (JDK),以便您可以编译和测试 Java 应用程序;还包括更多工具。

  • Java 运行时环境 (JRE),包括 Java 虚拟机 (JVM) 和生产和开发环境所需的所有类库。

  • 一个开发环境——Google 自己的开发人员使用 Eclipse,因此您可能也想效仿。或者,您可以安装 GWT4NB 并进行一些调整和修改,并使用 NetBeans,另一个流行的开发环境。

GWT 本身重约 27MB;下载后,使用以下命令将其解压到您喜欢的任何位置tar jxf ../gwt-linux-1.5.3.tar.bz2。无需进一步的安装步骤。您可以从任何目录使用 GWT。

对于本文,我使用了 Eclipse。对于更严肃的工作,您可能还需要其他一些附加组件,例如数据工具平台 (DTP)、Eclipse Java 开发工具 (JDT)、Eclipse 建模框架 (EMF) 和图形编辑框架 (GEF),但您可以使用 Eclipse 自己的软件更新工具轻松添加这些(以及更多)(您可以在 Eclipse 的主菜单上找到它,在帮助下——而且,我不知道为什么它位于那里)。

在开始一个项目之前,您应该了解 GWT 的四个组件

  • 当您开发应用程序时,GWT 在托管模式下运行,并提供一个 Web 浏览器(和一个嵌入式 Tomcat Web 服务器),这使您可以像最终用户看到的那样测试您的 Java 应用程序。请注意,您将能够使用您的开发套件的交互式调试功能,因此您可以忘记在 JavaScript 代码中放置 alert() 命令。

  • 为了帮助您构建界面,有一个 Web 界面库,它允许您创建和使用 Web 浏览器小部件,例如标签、文本框、单选按钮等等。您将使用这些小部件进行 Java 编程,编译过程会将它们转换为等效的 HTML 小部件。

  • 由于在客户端浏览器中运行的是 JavaScript,因此需要一个 Java 模拟库,它提供最常见的 Java 标准类的 JavaScript 等效实现。请注意,并非所有 Java 都可用,并且对您可以使用的类有所限制。如果您想使用不可用的类,您可能必须自己编写代码。截至 1.5 版本,GWT 涵盖了 JRE 的大部分内容。此外,截至 1.5 版本,GWT 支持使用 Java 5。

  • 最后,为了部署您的应用程序,有一个 Java 到 JavaScript 的编译器(转换器),您将使用它来生成最终的 Web 代码。当然,您稍后需要将生成的代码、JavaScript、HTML 和 CSS 放在您的 Web 服务器上。

如果您像大多数程序员一样,您可能会对转换后的应用程序的性能感到好奇。然而,GWT 生成的超紧凑代码可以进一步压缩和缓存,因此最终用户将只下载几十 KB 的最终代码,而且只下载一次。此外,在 1.5 版本中,生成的代码质量正在接近(甚至超过)手写 JavaScript 的质量,尤其是对于较大的项目。最后,因为您不需要浪费时间为每个现有的 Web 浏览器进行调试,您将有更多时间用于应用程序开发本身,这使您可以生成更多功能和更好的应用程序。

使用 GWT 需要学习几个包。最重要的包是

  • com.google.gwt.http.client:提供用于发出 HTTP 请求和处理接收到的响应的客户端类。如果您需要自己进行一些 AJAX,超出 GWT 本身完成的调用范围,您将使用它。

  • com.google.gwt.i18n.client:提供国际化支持。如果您正在开发一个将在多种语言中可用的系统,您将需要它。

  • com.google.gwt.json.client 和 com.google.gwt.xml.client:用于解析和读取 XML 和 JSON 数据。

  • com.google.gwt.junit.client:用于构建自动化 JUnit 测试。

  • com.google.gwt.user.client.ui:提供面板、按钮、文本框和所有其他用户界面元素和类。您肯定会使用这些。

  • com.google.gwt.user.client.rpc 和 com.google.gwt.user.server.rpc:这些与远程过程调用 (RPC) 有关。GWT 允许您透明地调用服务器代码,就好像客户端与服务器位于同一台机器上一样。

您可以在线找到有关这些包和其他包的信息,网址为 google-web-toolkit.googlecode.com/svn/javadoc/1.5/index.html

GWT 示例

现在,让我们转到一个实际示例。创建一个新项目是通过命令行而不是从 Eclipse 内部完成的。为您的项目创建一个目录,然后cd到它。然后在其中创建一个项目,使用

/path/to/GWT/projectCreator -eclipse ProjectName

接下来,创建一个基本的空应用程序,使用

/path/to/GWT/applicationCreator -eclipse ProjectName \
         com.CompanyName.client.ApplicationName

然后,打开 Eclipse,转到文件→导入→常规,选择将现有项目导入工作空间,然后选择您在其中创建项目的目录。不要选中将项目复制到工作空间框,以便将项目保留在您创建的目录中。

完成此操作后,您将能够编辑 HTML 和 Java 代码,添加新类并在托管模式下测试您的程序,如前所述。当您对最终产品感到满意时,您可以编译它(在您创建原始项目时生成了一个适当的脚本)并将其部署到您的 Web 服务器。

让我们做一个示例混搭。我们将有一个文本字段,用户将在其中键入一些内容,我们将查询一个服务器(好吧,只有一个服务器,这不是一个很大的混搭,但这个概念可以很容易地扩展)并显示返回的数据。当然,对于实际应用程序,我们不会显示原始数据,而是对其进行进一步处理。示例项目本身将被称为 exampleproject,其入口点将是 example,请参见列表 1 和图 1。

Web 2.0 Development with the Google Web Toolkit

图 1. 最近导入的项目——代码只显示欢迎消息。

列表 1. 项目必须手动创建,在 Eclipse 外部,然后导入到其中。

# cd
# md examplefiles
# cd examplefiles
# ~/bin/gwt/projectCreator -eclipse exampleproject
Created directory ~/examplefiles/src
Created directory ~/examplefiles/test
Created file ~/examplefiles/.project
Created file ~/examplefiles/.classpath

# ~/bin/gwt/applicationCreator -eclipse exampleproject \
       com.kereki.client.example
Created directory ~/examplefiles/src/com/kereki
Created directory ~/examplefiles/src/com/kereki/client
Created directory ~/examplefiles/src/com/kereki/public
Created file ~/examplefiles/src/com/kereki/example.gwt.xml
Created file ~/examplefiles/src/com/kereki/public/example.html
Created file ~/examplefiles/src/com/kereki/client/example.java
Created file ~/examplefiles/example.launch
Created file ~/examplefiles/example-shell
Created file ~/examplefiles/example-compile

根据 Google Web Toolkit 网站上的入门说明,您应该单击运行按钮以在托管模式下开始运行您的项目,但我发现以调试模式运行它更实用。转到运行→调试,然后启动您的应用程序。将出现两个窗口:开发 shell 和包装器 HTML 窗口,一个特殊版本的 Mozilla 浏览器。如果您进行任何代码更改,您不必关闭它们并重新启动应用程序。只需单击刷新,您将运行新版本的代码。

Web 2.0 Development with the Google Web Toolkit

图 2. 首次在托管模式下运行创建的应用程序

现在,让我们进行更改。因为我们正在使用 JSON 和 HTTP,我们需要添加一对行

<inherits name='com.google.gwt.json.JSON'/>

<inherits name='com.google.gwt.http.HTTP'/>

到 example.gwt.xml 文件。我们将重写主代码并添加几个包以调用提供 JSON 输出的服务器(请参阅“同源策略”侧边栏)。为此,向客户端添加两个类:JSONRequest 和 JSONRequestHandler;它们的代码显示在列表 2 和 3 中。

同源策略

同源策略 (SOP) 是一种安全限制,它基本上阻止从特定来源加载的页面访问来自不同来源的页面。来源是指三元组:协议 + 主机 + 端口。在 http://www.mysite.com:80/some/path/to/a/page 中,协议是 http,主机是 www.myhost.com,端口是 80。SOP 将允许访问来自 http://www.mysite.com:80 的任何文档,但不允许访问 https://www.mysite.com:80/something(不同的协议)、http://dev.mysite.com:80/something(不同的主机)或 http://www.mysite.com:81/something(不同的端口)。

为什么这是一个好主意?如果没有它,来自特定来源的 JavaScript 可能可以访问来自另一个来源的数据并秘密地操纵它。这将是终极网络钓鱼。您可能正在查看一个合法的、有效的、真实的页面,但它可能受到第三方的监视。有了 SOP,您可以确定您正在查看的任何内容都是由真实来源发送的。不可能有来自其他来源的任何代码。

当然,对于 GWT 来说,这是一个有点麻烦的事情,因为它意味着客户端应用程序不能简单地连接到任何其他服务器或 Web 服务以从中获取数据。至少有两种方法可以解决这个问题:一种特殊的、更简单的方法,只允许获取 JSON 数据,或者一种更复杂的解决方案,这意味着编码服务器端代理。您的客户端调用代理,代理调用服务。这两种解决方案都在 Google Web Toolkit 应用程序 一书中进行了解释(请参阅“资源”)。在本文中,我们使用 JSON 方法,您可以在 www.gwtsite.com/code/webservices 找到源代码。

简单的 JSON 方法需要一个特殊的回调例程,这可能是一个障碍。然而,许多站点都实现了这一点,包括 Amazon、Digg、Flickr、GeoNames、Google、Yahoo! 和 YouTube,并且该方法正在流行,因此您很可能能够找到一个合适的服务。

列表 2. JSONRequest 类的源代码

package com.kereki.client;
public class JSONRequest {
    public static void get(String url,
                           JSONRequestHandler handler) {
        String callbackName = "JSONCallback"+handler.hashCode();
        get(url+callbackName, callbackName, handler);
    }

    public static void get(String url, String callbackName,
                           JSONRequestHandler handler) {
        createCallbackFunction(handler, callbackName);
        addScript(url);
    }

    public static native void addScript(String url) /*-{
        var scr = document.createElement("script");
        scr.setAttribute("language", "JavaScript");
        scr.setAttribute("src", url);
        document.getElementsByTagName("body")[0].appendChild(scr);
    }-*/;

    private native static void createCallbackFunction(
                                   JSONRequestHandler obj,
                                   String callbackName) /*-{
        tmpcallback = function(j) {
            obj.@com.kereki.client.JSONRequestHandler::
                onRequestComplete(
                    Lcom/google/gwt/core/client/JavaScriptObject;)(j);
        };
        eval( "window." + callbackName + "=tmpcallback" );
    }-*/;
}

请注意,最后两个方法是用 JavaScript 而不是 Java 编写的;JavaScript 代码写在 Java 注释中。JavaScript 中的特殊 @id... 语法用于从 JavaScript 访问 Java 方法和字段。当应用程序编译时,此语法由 GWT 转换为正确的 JavaScript。有关更多信息,请参阅 GWT 文档。

列表 3. JSONRequestHandler 类的源代码

package com.kereki.client;
import com.google.gwt.core.client.JavaScriptObject;
public interface JSONRequestHandler {
    public void onRequestComplete(JavaScriptObject json);
}

您可以在 www.gwtsite.com/code/webservices 找到此列表和前一个列表的代码。

让我们选择完全使用 GWT 代码创建屏幕。按钮将向服务器(在本例中为 Yahoo! 新闻)发送请求,该服务器提供带有 JSON 结果的 API。当答案到来时,我们将在文本区域中显示接收到的代码。完整代码如列表 4 所示,图 3 显示了正在运行的程序。

列表 4. 主程序的源代码

package com.kereki.client;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.user.client.ui.*;
import com.google.gwt.json.client.*;
import com.google.gwt.http.client.URL;
import com.kereki.client.JSONRequest;
import com.kereki.client.JSONRequestHandler;

public class example implements EntryPoint {
    public void onModuleLoad() {
        final TextBox tbSearchFor = new TextBox();

        final TextArea taJsonResult = new TextArea();
        taJsonResult.setCharacterWidth(80);
        taJsonResult.setVisibleLines(20);

        final HorizontalPanel hp1 = new HorizontalPanel();

        Button bGetNews = new Button("Get news!",
            new ClickListener() {
                public void onClick(Widget sender) {
                    JSONRequest.get(
                        "http://search.yahooapis.com/"+
                        "NewsSearchService/V1/newsSearch?"+
                        "appid=YahooDemo&query="+
                        URL.encode(tbSearchFor.getText())+
                        "&results=2&language=en"+
                        "&output=json&callback=",
                        new JSONRequestHandler() {
                            public void onRequestComplete(
                                    JavaScriptObject json) {
                                JSONObject jj= new JSONObject(json);
                                taJsonResult.setText(jj.toString());
                            };
                        }
                    );
                }
            });

        hp1.add(new Label("Search for:"));
        hp1.add(new HTML("&nbsp;",true));
        hp1.add(tbSearchFor);
        hp1.add(new HTML("&nbsp;",true));
        hp1.add(bGetNews);

        RootPanel.get().add(hp1);
        RootPanel.get().add(new HTML("<br>",true));
        RootPanel.get().add(taJsonResult);
    }
}

列表 4 中的代码显示了对单个服务的访问,但是很容易同时连接到多个来源并生成新闻混搭。

Web 2.0 Development with the Google Web Toolkit

图 3. 在托管模式下运行的应用程序

在测试应用程序之后,就该分发它了。转到您创建项目的目录,运行编译脚本(在本例中为 example_script.sh),并将生成的文件复制到您的服务器的 Web 页面目录。在我的情况下,使用 OpenSUSE,它是 /srv/www/htdocs,但在其他发行版中,它可能是 /var/www/html(列表 5)。用户可以通过导航到 http://127.0.0.1/com.kereki.example/example.html 来使用您的应用程序,但当然,您可能会选择另一个路径。

列表 5. 编译代码并将文件部署到您的服务器

# cd ~/examplefiles/
# sh ./example-compile
Output will be written into ./www/com.kereki.example
Copying all files found on public pathCompilation succeeded
# sudo cp -R ./www/com.kereki.example /srv/www/htdocs/
结论

我们编写了一个 Web 页面,但从未编写任何 HTML 或 JavaScript 代码。此外,我们使用高级语言 Java,使用现代开发环境 Eclipse,充满了帮助和调试工具来完成编码。最后,我们的程序看起来与经典的 Web 页面截然不同。它没有全屏刷新,用户体验将更类似于桌面程序。

GWT 是一个非常强大的工具,允许您将当前的软件工程技术应用于缺乏良好、可靠的开发工具的领域。能够应用高级现代语言 Java 来解决客户端和服务器问题,并且能够忘记浏览器怪癖和不兼容性,应该足以让您想尝试一下 GWT。

资源

Google Web Toolkit 应用程序,Ryan Dewsbury 著,Prentice-Hall,2008 年。

Google Web Toolkit for AJAX,Bruce Perry 著,PDF 版,O'Reilly,2006 年。

Google Web Toolkit Java AJAX 编程,Prabhakar Chaganti 著,Packt Publishing,2007 年。

Google Web Toolkit 解决方案:酷炫且有用的东西,David Geary 和 Rob Gordon 著,PDF 版,Prentice Hall,2007 年。

Google Web Toolkit 解决方案:更多酷炫且有用的东西,David Geary 和 Rob Gordon 著,Prentice Hall,2007 年。

Google Web Toolkit——消除 AJAX 的痛苦,Ed Burnett 著,PDF 版,The Pragmatic Bookshelf,2007 年。

GWT in Action:使用 Google Web Toolkit 轻松实现 AJAX,Robert Hanson 和 Adam Tacy 著,Manning,2007 年。

AJAX:Web 应用程序的新方法:www.adaptivepath.com/ideas/essays/archives/000385.php

AJAX:入门:developer.mozilla.org/en/docs/AJAX:Getting_Started

AJAX 教程:www.xul.fr/en-xml-ajax.html

Apache 2.0 开源许可证:code.google.com/webtoolkit/terms.html

Eclipse:www.eclipse.org

Google Web Toolkit:code.google.com/webtoolkit

GWT4NB,用于 GWT 与 NetBeans 协同工作的插件:https://gwt4nb.dev.java.net

Java SE(标准版):java.sun.com/javase

Java 开发工具包 (JDK):java.sun.com/javase/downloads/index.jsp

JSON:www.json.org

JSON:XML 的精简替代品:www.json.org/xml.html

NetBeans:www.netbeans.org

同源策略,来自维基百科:en.wikipedia.org/wiki/Same_origin_policy

Web 2.0,来自维基百科:en.wikipedia.org/wiki/Web_2

什么是 Web 2.0?,作者:Tim O'Reilly:www.oreillynet.com/pub/a/oreilly/tim/news/2005/09/30/what-is-web-20.html

XML:www.xml.org

Federico Kereki 是一位乌拉圭系统工程师,在大学教学、开发和咨询工作以及撰写文章和课程材料方面拥有 20 多年的经验。他使用 Linux 已经很多年了,并在多家不同的公司安装了它。他对 Linux 机器的更好的安全性和性能特别感兴趣。

加载 Disqus 评论