天天看點

GWT how to work?

教你走下去,看看GWT:

在通往建立在使用者 Web 浏覽器中運作的動态 Java™Script 應用程式的征途中,Google Web Toolkit(GWT)是舉足輕重的一步。使用 GWT,開發人員可以利用熟知的 Java 技術設計使用者界面(UI)和事件模型,而 GWT 會完成使代碼對所有主流浏覽器友好的繁重工作。這是本系列的第一篇文章,介紹 GWT 基礎知識,包括 GWT 是怎樣使您建立一個 Asynchronous JavaScript + XML(Ajax)應用程式,而且依然在 Java 語言中編寫代碼的。探索如何建立和運作一個小型的樣本 GWT 應用程式 —— 近來十分流行的一項 Web 2.0 新業務,稱為 Slicr,線上銷售比薩餅。

GWT 使得建構富 Ajax 浏覽器客戶機界面比建構傳統 Java GUI 界面還要輕松。然而,即使是 GWT 這樣出色的技術也無法獨自建構出一個完整的 Web 應用程式。您還必須有一個伺服器上的資料存儲和某種類型的架構,以便将資料轉換成 GWT 可從伺服器傳遞給其客戶的 Java 對象。在這一系列文章中,您将使用 Apache Derby 這個 100% 純 Java 資料庫,可将其嵌入與其餘伺服器端代碼相同的 Java 虛拟機(JVM)之中。

本系列的第一篇文章主要探讨 GWT。在這裡,您将了解如何設定 GWT,并建立一個簡單的客戶機界面來響應使用者的操作。後續文章将為您展示如何設定 Derby 資料庫,并将 GWT 前端與基于 Derby 的後端連接配接。最終,您将學會如何将在開發環境之外部署您的系統。

檢視 Ajax 資源中心,這是您的一站式 Ajax 程式設計模型資訊中心,包括文章和教程、新手入門、推薦閱讀清單等。是您了解 Ajax 相關參考資源的最佳去處。

Google Web Toolkit 是什麼?

借助于 GWT,您可以使用 Java 程式設計語言開發 Ajax 應用程式。Ajax 應用程式的特色就是豐富、互動式的環境,往往與傳統 UI 應用程式相關聯。圖 1 展示了一個示例 GWT 界面,它效仿了桌面電子郵件應用程式。這個示範程式可在 GWT 的 Web 站點看到。

圖 1. GWT 電子郵件示範

GWT 電子郵件示範

GWT 最獨到的特性就在于:您可以建立 Ajax 應用程式,同時依然使用 Java 語言編寫代碼。您可以使用自己喜愛的 Java 內建化開發環境(IDE),而更好的事情是,還可以在 Java IDE 中調試您的客戶機。可以使用 Java 對象在客戶機與伺服器之間通信,這樣的通信在客戶機中比使用 Java applet 時要輕量得多。

從根本上來說,GWT 是一種編譯器。它将您編寫的 Java 代碼轉換成 JavaScript 代碼,這些代碼随後會插入 HTML 頁面,并用于運作應用程式的用戶端。這樣的功能使您免于處理在多種浏覽器上支援 JavaScript 代碼的細枝末節,進而使您能夠專注于程式的界面和互動之上。

當然,如果編譯器是 GWT 提供的惟一功能,那也不會令人太過興奮。幸運的是,它給我們帶來的不僅如此。有了 GWT,編譯器僅僅是一種傳遞整個客戶機/伺服器應用程式架構的機制。其特性包括:

* 一組标準 UI 小部件(widget),外觀良好、靈活性高,并且已進行了調優,可在所有主流浏覽器(包括 Safari 和 Opera)中工作。

* 一種完全在用戶端捕捉并響應事件的事件機制。

* 一個管理 Web 應用程式與伺服器間的異步調用的架構。

* 一種建立有狀态浏覽器曆史記錄的機制,以使您的 Ajax 應用程式不會因為有可能出現的後退(Back) 按鈕行為而變得混亂。

* 一個使用 JUnit 為客戶機應用程式編寫單元測試的測試架構。

本系列将探索上述特性中的大多數。但首先請下載下傳和安裝 GWT。

擷取 GWT

在本文撰寫之時,GWT 的最新版本是 V1.2(本文末尾處的 參考資料 部分給出了下載下傳站點的連結)。Microsoft® Windows® XP、Windows 2000、運作 GTK+ 2.2.1 或更新版本的 Linux® 系統和 Mac SO X 完全支援 GWT。

您下載下傳到的是一個壓縮檔案。解壓縮此檔案,然後将得到的目錄放在友善的位置。根據版本的不同,各釋出版的細節可能會略有不同,但基本元素如下:

* 三個 .jar 檔案:名為 gwt-user.jar 的檔案包含您将需要在項目 classpath 中使用的使用者類,而另一個包含大量編譯器代碼,檔案名為 gwt-dev-windows.jar 或 gwt-dev-linux.jar。第三個檔案 gwt-servlet.jar 用于部署。

* 三個指令行實用工具:applicationCreator、junitCreator 和 projectCreator。(在 Windows 上,它們的字尾為 .cmd。)稍後将介紹更多相關内容。

* 一個樣本代碼目錄。

在使用 GWT 時,還有其他一些檔案會放在 GWT 主目錄中,用于管理臨時檔案。

建立一個項目

既然您已經下載下傳好了一切,那麼接下來的首要任務就是建立一個項目。您将為近來十分流行的一項 Web 2.0 新業務(稱為 Slicr)建構一個線上站點,線上銷售比薩餅。根據您是否計劃使用 IDE,設定 GWT 項目的方法的具體細節會有所不同。就本文目的而言,您将使用 Eclipse,因為它是免費的,而且直接被 GWT 指令行實用工具所支援。

首先使用那些指令行實用工具來建立您的 Eclipse 項目:

1. 在您的硬碟上任意選擇一個友善的位置,建立一個名為 slicr 的新目錄。

2. 在新的 slicr 目錄下打開一個指令行提示符。

3. 鍵入以下指令(您需要更改斜線和與您的作業系統習慣不符的所有符号):

<GWT_HOME>/projectCreator -eclipse slicr

(說明:這裡的gwt_home是要寫出具體的内容)

這條指令将建立 GWT 項目所需的最基本内容。得到的結果是一個新的 src 子目錄,另外還有新的 .project 和 .classpath 檔案。

您可以就這樣先使用建立好的項目,但 GWT 希望能有進一步的結構,您可以通過如下指令來設定它:

<GWT_HOME>/applicationCreator -eclipse slicr com.ibm.examples.client.Slicr

(說明:gwt_home同上)

-eclipse slicr 參數是 Eclipse 項目的名稱,必須與您在 projectCreator 中使用的相同。(如果不同,在 Eclipse 内啟動您的 GWT 應用程式時就會出問題。)最後一個參數是您的應用程式的主類的完全限定類名。需要将最低級别的包命名為 client,但除此之外,您都可以自由選擇。

這條指令設定了一些檔案。.java 檔案是您的主類(以及主類必須建立的所有父目錄)。您得到了一個與 client 位于同一級别的 public 目錄,此目錄包含一個名為 Slicr.html 的檔案。上級目錄包含一個名為 Slicr.gwt.xml 的重要檔案。GWT 還會建立一個供 Eclipse 使用的 Slicr.launch 檔案以及一些 shell 腳本。

将項目遷移到 Eclipse 中

離成功不遠了!現在,您必須将項目遷移到 Eclipse 中。

1. 打開 Eclipse,然後單擊 File > Import。

2. 在出現的視窗中展開 General 樹,然後選擇 Existing Projects into Workspace。

3. 單擊 Next,然後單擊 Browse 來選擇 Slicr 根目錄。

4. 選中項目,確定未 設定 Copy projects into workspace 選項,您并不想移動項目。

這個過程會将您的代碼置入 Eclipse,在這裡您可以好好觀察代碼。GWT 已建立了三個重要的檔案。第一個位于 com.ibm.examples 包中,名為 slicr.gwt.xml。這是您的 XML 配置檔案。現在,它看上去如清單 1 所示。

清單 1. slicr.gwt.xml

<module>

<inherits name='com.google.gwt.user.User'/>

<entry-point class='com.ibm.examples.client.Slicr'/>

</module>

在這個 XML 文檔中,您可以為 GWT 應用程式定義子產品。子產品 是 GWT 代碼的基本機關,被客戶機使用的 HTML 頁面所引用。

public 目錄中建立的第二個重要的檔案是 Slicr.html 檔案。這是實際上作為 Web 應用程式的首頁發送給客戶機的 .html 檔案。預設情況下包含大量您不需要的注釋。此檔案的核心如清單 2 所示。

清單 2. Slicr.html

<html>

<head>

<title>Wrapper HTML for Slicr</title>

<meta name='gwt:module'

content='com.ibm.examples.Slicr'>

</head>

<body>

<script language="javascript"

src="gwt.js"></script>

<iframe id="__gwt_historyFrame"

style="width:0;height:0;border:0"></iframe>

<h1>Slicr</h1>

<p>

This is an example of a host page for

the Slicr application.

</p>

<table align=center>

<tr>

<td id="slot1"></td>

<td id="slot2"></td>

</tr>

</table>

</body>

</html>

在這一方面,要認識到的最重要的一點就是:最終它将是一個普通的 .html 檔案。盡管在其中包含您需要的任何 HTML。在這個 .html 檔案中,有四個元素将其辨別為由 GWT 使用的檔案。包括:

* meta 标記: name 屬性必須出現,content屬性是子產品的完全限定邏輯名。(也就是包含 XML 子產品檔案的包再加上 XML 的檔案名,不帶擴充名。)該标記将您的 HTML 頁面與特定子產品相關聯。調用頁面将啟動子產品。(明确地說,該子產品中的所有入口點類都被初始化。)

* script 标記:此标記載入一個名為 gwt.js 的檔案,這是将 GWT Java 代碼轉換為 JavaScript 代碼時建立的檔案之一。由于此檔案控制所有客戶機代碼的加載,是以要運作程式,将這個檔案包含進來是非常重要的。

* iframe 标記:準确地包含此标記(準确地編寫)允許您的 Web 程式記錄曆史情況和狀态,這也就意味着,您的 GWT 應用程式不會禁用使用者的後退(Back)按鈕。

* td 标記:這個特殊 .html 檔案中的這些标記包含 JavaScript 辨別符。這方面沒有什麼特别不尋常的東西,但随後您會看到,GWT 将這些辨別符用作放置元素的地方。

樣本 Java 啟動類

GWT 還為您建立了一個樣本 Java 啟動類,如清單 3 所示。

清單 3. 樣本 Java 啟動類

public class Slicr implements EntryPoint {

public void onModuleLoad() {

final Button button = new Button("Click me");

final Label label = new Label();

button.addClickListener(new ClickListener() {

public void onClick(Widget sender) {

if (label.getText().equals(""))

label.setText("Hello World!");

else

label.setText("");

}

});

RootPanel.get("slot1").add(button);

RootPanel.get("slot2").add(label);

}

}

目前,關于這個類隻有幾件事情需要注意。Java 類位于您的 client 包中,表示着它意圖被 GWT 編譯到 JavaScript 代碼中。這會給哪些内容可放在此檔案中造成了一些限制,但在大多數時候,其中都是基本的 Java 1.4 代碼。所建立的類實作接口 EntryPoint,此接口僅定義了一個方法,即 onModuleLoad()。在載入引用了此子產品的 HTML 頁面時,GWT 将自動調用此方法。

這個特殊的 EntryPoint 非常簡單。它的頭兩行定義了一個按鈕和一個标簽。在最後兩行中,它使用 RootPanel.get() 方法,将這些小部件與特定的 HTML 頁面元素相關聯。傳遞給此方法的參數是元素的 JavaScript ID,與其在 HTML 頁面中的定義相同。

在頭兩行和最後兩行之間,您使用 Swing 中用于綁定事件的同一風格來定義一個事件偵聽器。在本例中,當您的按鈕被單擊時,ClickListener 将被調用,并簡單地來回切換标簽小部件中的文本。

運作您的 GWT 程式

現在,您可以嘗試運作 GWT 建立的樣本程式。有兩種不同的方法可以運作 GWT 程式:Web 模式 和 托管模式(hosted mode)。Web 模式是完全部署模式,在将您的 GWT 程式編譯為 JavaScript 代碼之後,您将使用這種模式。

在開發過程中使用的是托管模式,它是一個模拟器,通過它可以同時模拟客戶機和伺服器代碼,在開發時使部署大為簡化。(托管模式目前在 Mac OS X 上尚不可用。)如果您正在使用帶有調試器的 IDE,那麼可以在調試器下于托管模式中運作您的 GWT 程式,這允許您設定斷點和變量觀察(variable watch),甚至可以在即将編譯為 JavaScripy 代碼的客戶機代碼部分中進行設定。毫無疑問,這非常酷,也的确非常有用。在 GWT 中工作時,您将在托管模式中花費許多時間。

有多種調用托管模式的方法,您可任選其中的一種。前面您運作的 applicationCreator 腳本建立了一個 Slicr-shell 腳本,可通過指令行調用它來啟動托管模式。在上文中,您看到了如何将您的 GWT 項目導入 Eclipse。在 Eclipse 項目中,可以從 Run 菜單或工具欄選擇 Debug 或 Run。在出現的視窗中,單擊 Java Application 可看到 Slicr 選項。該選項是由 GWT 建立的一個 Slicr.launch 檔案提供的,您會将該檔案與項目的其餘部分一起導入 Eclipse。這個啟動檔案為 Eclipse 描述了 classpath 和啟動類。當然,一旦運作了啟動程式(launcher),在您單擊工具欄按鈕時它将是預設選擇。圖 2 展示了實際情況下,該視窗的運作效果。

圖 2. 調用托管模式

調用托管模式

在托管模式下運作時,将出現兩個視窗。(請注意,托管模式設定初始化需要占用一分鐘左右的時間,特别是在第一次運作時。)第一個視窗如圖 3 所示,名為 Google Web Toolkit Development Shell / Port 8888。此視窗包含來自 GWT 的錯誤和日志消息。使用工具欄,您可以打開一個新的托管浏覽器,也可以展開、折疊和清除螢幕上的日志記錄。

圖 3. 托管模式的 shell 視窗

托管模式的 shell 視窗

第二個視窗如圖 4 所表,是模拟浏覽器。如您所見,它包含來自 slicr.html 頁面的靜态 HTML,還包括 Slicr.java EntryPoint 類中建立的按鈕小部件。單擊按鈕将切換标簽。如果您在設定步驟中進行了某些錯誤的操作,那麼就不會看到這個視窗,而是在 shell 視窗中看到一條錯誤消息。驗證所有名稱都是正确的。(特别要主要檢視 .launch 檔案,驗證它指定了正确的項目目錄。)

圖 4. 托管模式模拟浏覽器

托管模式模拟浏覽器

設定一個簡單的客戶機

就本文而言,我們集中關注在螢幕上獲得小部件,并提供一點互動性。您最終将得到如圖 5 所示的螢幕,大體上來說,它簡單而又實用。

圖 5. Slicr

Slicr

要在頁面載入時建立這些小部件,您必須将代碼放在 EntryPoint 類的 onModuleLoad() 方法中。清單 4 定義了一些執行個體資料成員和一個調用 helper 來建構各面闆的頂級方法。

清單 4. Module 加載事件處理程式

private DockPanel panel;

private List clearables;

public void onModuleLoad() {

clearables = new ArrayList();

initDockPanel();

panel.add(buildActionPanel(), DockPanel.SOUTH);

panel.add(buildPizzaTypePanel(), DockPanel.WEST);

panel.add(buildToppingPanel(), DockPanel.EAST);

RootPanel.get("slicr").add(panel);

}

設定小部件

您将所有一切都放在一個 DockPanel 内,這是使用 BorderLayout 的 Swing 面闆的 GWT 等價部分。Swing 具有一個 Panel 類和多個布局管理器,而 GWT 具有多個 panel 子類,每個子類都使用自己的算法來安排子小部件的布局。其他面闆類還包括 SimplePanel、HTMLTable、FlowPanel 和 StackPanel。建立停靠面闆并不困難:setter 會幫助您完成所需的一切工作, 如清單 5 所示。

清單 5. 初始化主面闆

private void initDockPanel() {

panel = new DockPanel();

panel.setBorderWidth(1);

panel.setSpacing(5);

}

建立南側(按鈕)面闆

首先要定義南側(按鈕)面闆,因為在請求角落處時,DockPanel 是最先出現、最先被服務的。通過這種方式,南側的小部件才會在整個面闆上運作。您将操作面闆建構為一個 HorizontalPanel,它大緻上相當于 Swing 中的一個框,如清單 6 所示。

清單 6. 南側(按鈕)面闆

public HorizontalPanel buildActionPanel() {

HorizontalPanel actions = new HorizontalPanel();

actions.setSpacing(10);

Button clear = new Button("Clear");

clear.addClickListener(new ClearClickListener());

Button newPizza = new Button("Another Pizza");

Button submitOrder = new Button("Submit");

actions.add(clear);

actions.add(newPizza);

actions.add(submitOrder);

return actions;

}

您使用了 GWT Button 小部件來建立三個按鈕,然後将其添加到面闆中。還為 Clear 按鈕建立了一個 ClickListener,稍後我們将定義它。GWT 劃分其事件偵聽器的方式與 Swing 不同:ClickListener 偵聽且僅偵聽滑鼠點選。(通常,您會看到偵聽器被定義為内嵌匿名類。我發現這種風格難于讀取和測試,是以我建立了一個有名稱的内部類。)

建立西側(比薩餅類型)面闆

帶有比薩餅類型的面闆并不複雜。需要使用 GWT RadioButton 小部件,如清單 7 所示。

清單 7. 西側(比薩餅類型)面闆

public static final String[] PIZZA_TYPES = new String[] {

"Thin Crust Medium", "Thin Crust Large",

"Thin Crust X-Large", "Thick Crust Medium",

"Thick Crust Large"

};

private VerticalPanel buildPizzaTypePanel() {

VerticalPanel pizzaTypes = new VerticalPanel();

HTML label = new HTML("<h2>Pizza</h2>");

pizzaTypes.add(label);

for (int i = 0; i < PIZZA_TYPES.length; i++) {

RadioButton radio = new RadioButton("pizzaGroup",

PIZZA_TYPES[i]);

clearables.add(radio);

pizzaTypes.add(radio);

}

return pizzaTypes;

}

稍後,您将對資料進行某些更聰明的處理。現在,您使用的是 VerticalPanel,它是 HorizontalPanel 的垂直對應部分。您還使用了 HTML 小部件,這隻是一個呈現 HTML 的标簽。(本質上,它是一個包圍着 HTML <span> 标記的包裝器。)RadioButton 構造函數接受兩個參數。第一個是單選按鈕組的字元串标簽,第二個是一個文本标簽。您将各個按鈕同時添加到面闆中和可清除項的執行個體清單中,這個清單将用于一個偵聽器之中。

建立東側(澆頭)面闆

澆頭面闆略微複雜一點。您需要允許使用者制作每一半具有不同澆頭的一張比薩餅。單擊與某種澆頭對應的按鈕會為兩半都選中澆頭,但每一半都可單獨選中或清除。您需要把一切排列整齊,是以使用一個網格,如清單 8 所示。

清單 8. 澆頭網格

public static final String[] TOPPINGS = new String[] {

"Anchovy", "Gardineria", "Garlic",

"Green Pepper", "Mushrooms", "Olives",

"Onions", "Pepperoni", "Pineapple",

"Sausage", "Spinach"

};

private VerticalPanel buildToppingPanel() {

VerticalPanel toppings = new VerticalPanel();

toppings.add(new HTML("<h2>Toppings</h2>"));

Grid topGrid = new Grid(TOPPINGS.length + 1, 3);

topGrid.setText(0, 0, "Topping");

topGrid.setText(0, 1, "Left");

topGrid.setText(0, 2, "Right");

for (int i = 0; i < TOPPINGS.length; i++) {

Button button = new Button(TOPPINGS[i]);

CheckBox leftCheckBox = new CheckBox();

CheckBox rightCheckBox = new CheckBox();

clearables.add(leftCheckBox);

clearables.add(rightCheckBox);

button.addClickListener(new ToppingButtonListener(

leftCheckBox, rightCheckBox));

topGrid.setWidget(i + 1, 0, button);

topGrid.setWidget(i + 1, 1, leftCheckBox);

topGrid.setWidget(i + 1, 2, rightCheckBox);

}

toppings.add(topGrid);

return toppings;

}

又一次地,您使用了 VerticalPanel 和 HTML 小部件。您将所有的東西都放在了一個 GWT Grid 中,是以在建立時必須設定好網格的大小。網格中的每個單元格都包含純文字或另外一個 GWT 小部件。對于每一行,建立按鈕和兩個複選框,然後将其指派給相應的單元格。為按鈕添加一個偵聽器,随後将複選框放在 clearable 清單中。

定義好的偵聽器

設定好小部件後,就該看看那兩個已經定義好的偵聽器了。其中較為簡單的一個是 Clear 按鈕的偵聽器。該按鈕僅僅是周遊 clearable 清單并清除所有内容,如清單 9 所示。

清單 9. 為 Clear 按鈕定義的偵聽器

private class ClearClickListener implements ClickListener {

public void onClick(Widget sender) {

for (Iterator iter = clearables.iterator(); iter.hasNext();) {

CheckBox cb = (CheckBox) iter.next();

cb.setChecked(false);

}

}

}

注意:在 GWT 中,RadioButton 實際上是 CheckBox 的子類。是以上述代碼不會觸發類轉換異常。

澆頭按鈕的偵聽器隻是稍微複雜一點。如果未選中任何相關複選框,則此偵聽器将選中兩個複選框。反之,則清除兩個複選框,如清單 10 所示。

清單 10. 為澆頭按鈕定義的偵聽器

private class ToppingButtonListener implements ClickListener {

private CheckBox cb1;

private CheckBox cb2;

public ToppingButtonListener(CheckBox cb1, CheckBox cb2) {

this.cb1 = cb1;

this.cb2 = cb2;

}

public void onClick(Widget sender) {

boolean unchecked = !cb1.isChecked() && !cb2.isChecked();

cb1.setChecked(unchecked);

cb2.setChecked(unchecked);

}

}

繼續閱讀