|
Google Web Toolkit(GWT)是一个开源 Java 软件开发框架,旨在帮助那些不甚精通浏览器的开发人员更容易地编写 Web 2.0 应用程序,即通过 AJAX 编写异步 web 应用程序,请参阅 Google Web Toolkit 站点。现在,编写动态 web 应用程序是一个单调乏味且容易出错的过程,需要花费 90% 的时间来处理 web 浏览器与平台之间细微的不兼容性,而且由于 JavaScript 不具有模块性,所以很难共享、测试和重用 AJAX 组件。GWT 不但克服了这些缺陷,而且仍然为用户提供了同样的动态且符合标准的体验。使用 Java 编程语言编写前端代码,然后 GWT 编译器将 Java 类转换成支持浏览器的 JavaScript 和 HTML。”
在本教程中,我们将学习如何将上述原理应用到实际的应用程序中。同时,还将向您介绍 NetBeans IDE 中对 GWT 的支持,以及如何使用这些特性构建简单的应用程序。
目录
|
|
 |
本教程所需要的软件
开始之前,需要在您的计算机中下载并安装以下软件:
- NetBeans IDE 6.0 Web & Java EE(下载)。
- Java Standard Development Kit(JDK™)5.0 版或 6.0 版(下载)
- Google Web Toolkit(下载)
- 在 IDE 中,打开 Plugin Manager(位于 Tools 菜单下)并安装 GWT 插件,如下图所示:

关于 GWT 的更多信息,请查阅 http://code.google.com/webtoolkit/。有关 IDE 对 GWT 支持的详细信息,请查阅 https://gwt4nb.dev.java.net/。如果熟悉 GWT,欢迎您为本教程所使用的 GWT 插件贡献代码。
注意:本教程将以 Google Web Toolkit: GWT Java AJAX Programming” 中介绍的一些示例为基础,该著作由 Prabhakar Chaganti 编写,并于 2007 年 2 月由 Packt Publishing 出版。
建立环境
首先使用 IDE 生成一个基本源结构。完成之后,我们将对其进行仔细研究,以理解 GWT 的内部工作原理。
创建 GWT 应用程序的源结构
应用程序的源结构必须包含 GWT JAR 文件,这是 GWT 模块项目配置文件,以及一些标准的认为因素,比如 Java 入口点。由于我们使用的是 IDE,所以不需要手动创建所有这些文件。而是通过一个向导来完成这些工作。特别地,在我们的 GWT 应用程序的上下文中,Web Application 向导最后的面板将非常有用。
- 选择 File > New Project 选项。在 Categories 列表中,选择 Web 选项。在 Projects 列表中,选择 Web Application 选项。单击 Next 按钮。
- 在 Name and Location 面板的 Project Name 字段中键入 HelloGWT。
- 将 Project Location 更改为计算机上的任意目录。
- 选择所使用的服务器。可以将 GWT 部署到 IDE 支持的任何服务器上。
- 将 Java EE Version 更改为 1.4,因为 GWT 不支持 Java EE 5。例如,Java EE 5 注释将会导致编译错误。单击 Next 按钮。
- 在 Frameworks 面板中,选择 GWT 选项,如下图所示:

该面板提供了以下字段:
- GWT Installation Folder:指定在本教程开始部分安装 Google Web Toolkit 的文件夹路径。如果指定了一个错误路径,将会出现红色的错误消息,而且不能完成向导。
- GWT Module:指定完成向导时 IDE 将要生成的项目模块的名称位置。项目模块是一个配置 GWT 应用程序的 XML 文件。例如,可以用它指定载入模块时 GWT 实例化的类。注意向导中的这个字段还决定了主要的应用程序包。默认情况下,主包为 org.yournamehere,而项目模块为 Main。基于本教程的目的,我们将保留默认设置。
- 单击 Finish 按钮。
IDE 将创建 HelloGWT 项目。项目包含所有的源代码、库和项目元数据,比如项目的 Ant 构建脚本。此项目将在 IDE 中打开。可以在 Projects 窗口中查看其逻辑结构(Ctrl-1):

右键单击项目并选择 Run 选项。IDE 将编译应用程序并创建一个 WAR 归档文件。该文件被传递给服务器。如果服务器未运行,那么会启动服务器。浏览器将会打开。应用程序显示在浏览器中:

单击按钮,它下面的文本就会消失:

在下一节中,将仔细研究生成的每个文件,并讨论如何创建上述的简单应用程序。
检查 GWT 应用程序的源结构
IDE 的 Web Application 向导为我们创建了几个源文件。现在,我们看一下这些文件,并研究它们是如何在 GWT 应用程序上下文中彼此关联的。
- Project module:Main.gwt.xml 文件,是应用程序主包中的一个 XML 文件,用于保存 GWT 项目所需的完整的应用程序配置信息。向导生成的默认项目模块如下所示:
<?xml version="1.0" encoding="UTF-8"?> <module> <inherits name="com.google.gwt.user.User"/> <entry-point class="org.yournamehere.client.MainEntryPoint"/> <!-- Do not define servlets here, use web.xml --> </module>
默认项目模块中包含以下元素:
- inherits:指定此模块继承的模块。在本文的简单示例中,我们只继承了 User 模块的功能性,已经构建在 GWT框架中构建了此模块。当应用程序变得更加复杂时,模块继承性提供了一种快速且高效的重用功能的方法。
- entry-point:载入模块时 GWT 将要实例化的类。
- Application Entry Point:MainEntryPoint 类是应用程序的入口点,在之前的项目模块中指定。它扩展 EntryPoint 类,并且当 GWT 框架载入 GWT 模块时,将对此类进行实例化并自动调用其 onModuleLoad() 方法。向导生成的默认入口点如下所示:
package org.yournamehere.client;
import com.google.gwt.core.client.EntryPoint; import com.google.gwt.user.client.ui.Button; import com.google.gwt.user.client.ui.ClickListener; import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.RootPanel; import com.google.gwt.user.client.ui.Widget;
public class MainEntryPoint implements EntryPoint {
/** Creates a new instance of MainEntryPoint */ public MainEntryPoint() { }
/** * The entry point method, called automatically by loading a module * that declares an implementing class as an entry-point */ public void onModuleLoad() { final Label label = new Label("Hello, GWT!!!"); final Button button = new Button("Click me!");
button.addClickListener(new ClickListener(){ public void onClick(Widget w) { label.setVisible(!label.isVisible()); } });
RootPanel.get().add(button); RootPanel.get().add(label); }
}
默认入口点类中的 onModuleLoad() 方法向应用程序添加以下内容:
- Label:创建一个新的 com.google.gwt.user.client.ui.Label,显示文本“Hello, GWT!!!”。通过最后一行代码 RootPanel.get().add(label) 将该标签添加到根面板。
- Button:创建一个新的 com.google.gwt.user.client.ui.Button,显示文本“Click me!”,与一个按钮侦听器结合在一起,由一个 com.google.gwt.user.client.ui.ClickListener 提供。按钮侦听器指定当单击按钮时隐藏该标签。通过最后一行代码 RootPanel.get().add(button) 将该按钮添加到根面板。
- Host Page:生成的 HTML 页面,名为 welcomeGWT.html,用来载入应用程序。web.xml 文件使用 welcome-file 元素指定主页是部署应用程序时浏览器中显示的初始页面。向导生成的默认主页如下所示:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <meta name='gwt:module' content='org.yournamehere.Main=org.yournamehere.Main'> <title>Main</title> </head> <body> <script language="javascript" src="org.yournamehere.Main/org.yournamehere.Main.nocache.js"></script> </body> </html>
默认主页中包含以下元素:
- meta:指向应用程序的项目目录。此标志是 HTML 页面和应用程序之间的连接点。
- script:从 GWT 框架的 Javascript 文件导入代码。此文件包含引导 GWT 框架所需的代码。它使用项目模块中的配置信息,然后动态地载入通过编译入口点而创建的 Javascript,从而呈现应用程序。Javascript 文件是在托管模式中运行应用程序时或编译应用程序时由 GWT 框架生成的。
创建 AJAX 随机引用生成器
在本节中,我们将在 web 页面上显示一个随机引用。此示例应用程序将使您更加熟悉 GWT 应用程序中的各个部分和模块。从存储在服务器上的引用列表中选择随机引用。我们的应用程序将每隔一秒检索服务器提供的随机引用,并以实际的 AJAX 样式在 web 页面上显示,也就是说不用刷新页面。
在创建此功能的过程中,我们将使用一个 GWT 服务。此处的服务与 web 服务毫无关系。一个服务是一些代码,客户机在服务器端调用这些代码来访问服务器的功能并在客户端显示。
生成服务桩(stub)
NetBeans GWT 插件提供了一个向导,该向导可以生成基本的服务类。在此小节中,将向您介绍该向导。
- 右键单击 HelloGWT 项目节点并选择 New > Other。在 New File 向导中,Google Web Toolkit 目录显示了一个名为“GWT RPC Service”的文件模板,如下图所示:

单击 Next 按钮。
- 可选地,填充生成的文件将要存储的子包。基于本教程的目的,我们在此处键入 sampleservice 作为 Subpackage 字段,如下图所示:

- 单击 Finish 按钮。
生成了前面的屏幕截图中显示的文件,如下图所示:

注意:需要进行细微的修改。在生成的实现类 GWTServiceImpl 中,需要导入 org.yournamehere.client.sampleservice.GWTService,而不是 org.yournamehere.client.GWTService。
检查生成的类
GWT RPC Service 向导创建了一些源文件。现在,我们看一下这些文件,并研究它们是如何在 GWT 服务中彼此关联的。
- GWTService:服务的客户端定义。
package org.yournamehere.client.sampleservice;
import com.google.gwt.user.client.rpc.RemoteService;
public interface GWTService extends RemoteService { public String myMethod(String s); }
- GWTServiceAsync:服务的异步版本的客户端定义。它提供了一个回调对象,支持服务器和客户机之间的异步通信。
package org.yournamehere.client.sampleservice;
import com.google.gwt.user.client.rpc.AsyncCallback;
public interface GWTServiceAsync { public void myMethod(String s, AsyncCallback callback); }
- GWTServiceImpl:实现接口的 servlet,提供通过 RPC 检索随机引用的功能。
package org.yournamehere.server.sampleservice;
import com.google.gwt.user.server.rpc.RemoteServiceServlet; import org.yournamehere.client.sampleservice.GWTService;
public class GWTServiceImpl extends RemoteServiceServlet implements GWTService {
public String myMethod(String s) { // Do something interesting with 's' here on the server. return "Server says: " + s; }
}
- GWTServiceUsageExample:生成的示例用户接口,作为一个测试客户机。它对服务进行实例化。
package org.yournamehere.client.sampleservice;
import com.google.gwt.core.client.GWT; import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.gwt.user.client.rpc.ServiceDefTarget; import com.google.gwt.user.client.ui.Button; import com.google.gwt.user.client.ui.ClickListener; import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.TextBox; import com.google.gwt.user.client.ui.VerticalPanel; import com.google.gwt.user.client.ui.Widget;
public class GWTServiceUsageExample extends VerticalPanel {
private Label lblServerReply = new Label(); private TextBox txtUserInput = new TextBox(); private Button btnSend = new Button("Send to server");
public GWTServiceUsageExample() {
add(new Label("Input your text: ")); add(txtUserInput); add(btnSend); add(lblServerReply);
// Create an asynchronous callback to handle the result. final AsyncCallback callback = new AsyncCallback() { public void onSuccess(Object result) { lblServerReply.setText((String)result); }
public void onFailure(Throwable caught) { lblServerReply.setText("Communication failed"); } };
// Listen for the button clicks btnSend.addClickListener(new ClickListener(){ public void onClick(Widget w) { // Make remote call. Control flow will continue immediately and later // 'callback' will be invoked when the RPC completes. getService().myMethod(txtUserInput.getText(), callback); } }); }
public static GWTServiceAsync getService(){ // Create the client proxy. Note that although you are creating the // service interface proper, you cast the result to the asynchronous // version of // the interface. The cast is always safe because the generated proxy // implements the asynchronous interface automatically. GWTServiceAsync service = (GWTServiceAsync) GWT.create(GWTService.class); // Specify the URL at which our service implementation is running. // Note that the target URL must reside on the same domain and port from // which the host page was served. // ServiceDefTarget endpoint = (ServiceDefTarget) service; String moduleRelativeURL = GWT.getModuleBaseURL() + "gwtservice"; endpoint.setServiceEntryPoint(moduleRelativeURL); return service; }
}
注意:继续之前,需要将上面的片段中的 moduleRelativeURL 字符串修改为如下代码,以包括我们的子包:
String moduleRelativeURL = GWT.getModuleBaseURL() + "sampleservice/gwtservice";
- 将入口点类的 onModuleLoad() 方法更改为:
public void onModuleLoad() { RootPanel.get().add(new GWTServiceUsageExample()); }
- 右键单击项目节点并选择 Run 选项。如果服务器未运行,那么服务器将启动。应用程序被部署到服务器。打开浏览器。显示一个文本字段。键入一些字符并单击按钮。出现一个带有您发送的消息的标签,如下图所示:

现在已经成功地对生成的服务进行了测试。在下一节中,我们将用这个服务做一些有用的工作,现在我们知道它能够正常工作了。
扩展生成的类
在本节中,我们对在上一小节检查的类进行修改和扩展。在这一小节的末尾,我们将完成 AJAX 随机引用生成器。
- 从 GWTService 移除 String 参数,完成后的接口如下:
public interface GWTService extends RemoteService { public String myMethod(); }
- 在异步服务中进行同样的操作,完成后的接口如下所示:
public interface GWTServiceAsync { public void myMethod(AsyncCallback callback); }
- 在 GWTServiceImpl 中,实现该接口:
public class GWTServiceImpl extends RemoteServiceServlet implements GWTService {
private Random randomizer = new Random(); private static final long serialVersionUID = -15020842597334403L; private static List quotes = new ArrayList();
static { quotes.add("No great thing is created suddenly - Epictetus"); quotes.add("Well done is better than well said - Ben Franklin"); quotes.add("No wind favors he who has no destined port - Montaigne"); quotes.add("Sometimes even to live is an act of courage - Seneca"); quotes.add("Know thyself - Socrates"); }
public String myMethod() { return (String) quotes.get(randomizer.nextInt(4)); }
}
注意:右键单击并选择 Fix Imports,使 IDE 创建一个正确的导入语句。进行这步操作时,请确保选择了 java.util.Random,而不是 com.google.gwt.user.client.Random。
- 最后,我们将相关的代码片段从生成的用例类中剪切并粘贴到入口点类中。首先,将 getService() 方法复制到入口点类中:
public static GWTServiceAsync getService(){ // Create the client proxy. Note that although you are creating the // service interface proper, you cast the result to the asynchronous // version of // the interface. The cast is always safe because the generated proxy // implements the asynchronous interface automatically. GWTServiceAsync service = (GWTServiceAsync) GWT.create(GWTService.class); // Specify the URL at which our service implementation is running. // Note that the target URL must reside on the same domain and port from // which the host page was served. // ServiceDefTarget endpoint = (ServiceDefTarget) service; String moduleRelativeURL = GWT.getModuleBaseURL() + "sampleservice/gwtservice"; endpoint.setServiceEntryPoint(moduleRelativeURL); return service; }
- 将入口点类的 onModuleLoad() 方法更改为:
/** * The entry point method, called automatically by loading a module * that declares an implementing class as an entry-point */
public void onModuleLoad() {
final Label quoteText = new Label();
Timer timer = new Timer() {
public void run() { //create an async callback to handle the result: AsyncCallback callback = new AsyncCallback() {
public void onFailure(Throwable arg0) { //display error text if we can't get the quote: quoteText.setText("Failed to get a quote"); }
public void onSuccess(Object result) { //display the retrieved quote in the label: quoteText.setText((String) result); } }; getService().myMethod(callback); } };
timer.scheduleRepeating(1000); RootPanel.get().add(quoteText);
}
注意:右键单击并选择 Fix Imports,使 IDE 创建一个正确的导入语句。进行这步操作时,请确保选择了 com.google.gwt.user.client.Timer,而不是 java.util.Timer。
- 删除 GWTServiceImpl。
- 右键单击项目节点并选择 Run 选项。当应用程序部署完成并打开浏览器时,每隔一秒您就会看到一个从服务器接收到的新引用。

在下一节中,我们旧爱那个使用样式表更改引用的外观。
定制外观和样式
在本节中,我们将一个样式表绑定到 HTML 主页。并在入口点类中引用它。具体来讲,我们将入口点类中的标签的样式名称设置为样式表中的样式名称。在运行时,GWT将此样式连接到标签,并在浏览器中显示一个定制的标签。
- 在 Web Pages 节点中,也就是与 welcomeGWT.html 文件相同的位置,创建一个 welcomeGWT.css 文件。要创建此文件,在 New File 向导中选择 Other > Cascading Style Sheet 选项。
- 将此内容粘贴到新样式表中:
.quoteLabel { color: white; display: block; width: 450px; padding: 2px 4px; text-decoration: none; text-align: center; font-family: Arial, Helvetica, sans-serif; font-weight: bold; border: 1px solid; border-color: black; background-color: #704968; text-decoration: none; }
现在样式表编辑器应该如下图所示:

- 将样式表绑定到 HTML 主页。同时,添加一些文本以向用户介绍此应用程序。HTML 页面中的新内容为下面的以粗体形式突出显示的部分。
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <meta name='gwt:module' content='org.yournamehere.Main=org.yournamehere.Main'> <title>Main</title> <link rel="stylesheet" type="text/css" href="welcomeGWT.css"> </head> <body> <script language="javascript" src="org.yournamehere.Main/org.yournamehere.Main.nocache.js"></script> <p>This is an AJAX application that retrieves a random quote from the Random Quote service every second. The data is retrieved and the quote updated without refreshing the page! </body> </html>
- 最后,在入口点类中,指定在样式表中定义的样式应该应用到标签上。新添加的内容为下面的以粗体形式突出显示的行:
public void onSuccess(Object result) { //display the retrieved quote in the label: quoteText.setText((String) result); quoteText.setStyleName("quoteLabel"); }
输入代码时,请注意代码完成(code completion)可以帮助您,它会建议完成代码的方式并显示相关的 Javadoc。

- 右键单击项目节点并选择 Run 选项。此时,标签显示为定制的样式,该样式是使用本小节中创建的样式表生成的。

结束语
在本教程中,我们学习了以下内容:
- Google Web Toolkit 应用程序中的典型应用程序源结构。
- Google Web Toolkit 的人为因素如何相互关联。
- 如何设置 IDE 以使用 Google Web Toolkit。
- IDE 中可用的工具有哪些,特别是使用 Google Web Toolkit 的工具。
最后,打开 Files 窗口(Ctrl-2)并展开 build 文件夹。(如果 build 文件夹没有显示,则需要再次构建该项目,从而让 IDE 重新生成 build 文件夹)。您应该看到与下图所示的内容:

当编译应用程序时 GWT 会自动生成该文件夹。文件夹包含客户机应用程序的可部署的版本。除了其他文件,它还包含以下文件:
- gwt.js:生成的 JavaScript 文件,包含载入和初始化 GWT 框架的引导代码。
- History.html:一个 HTML 文件,提供历史管理支持。
- xxx-cache.html 和 xxx-cache.xml:为每个浏览器生成一个 HTML 文件和 XML 文件。这些文件包含编译客户机和服务器包中的 Java 源文件时生成的 JavaScript 代码。每个 HTML 和 XML 文件集合代表一个支持的浏览器。
因此,综上所述,由于 GWT 框架处理浏览器相关的代码生成,以及创建较低层的 XmlHttpRequest API 代码,作为该框架的 用户,我们可以专注于期望的应用程序功能性。所以,正如教程开始部分所述,GWT 不但避免了与浏览器兼容性相关的头疼问题,同时还能为用户提供与典型 Web 2.0 程序相同的动态的、符合标准的体验。正如本教程所展示的,借助 GWT 框架,我们可以用 Java 编程语言编写完整的前端,因为您知道可以使用 GWT 编译器将 Java 类转换为浏览器支持的 JavaScript 和 HTML。而且,正如本教程所演示的,IDE 提供了一个完整的工具集,可以使所有这些工作变得简单高效,而不用对 GWT 应用程序的基本架构进行编码。
更多信息
|