Ajax技術是當前開發web應用的非常熱門的技術,也是Web 2.0的一個重要的組成部分。然而如果用傳統的方式Javascript進行Ajax開發的話,就會使得應用程序非常難以進行調試,從而降低了生產效率。Google最近推出的GWT有望為我們解決這個難題,GWT是一個開發Ajax應用的框架,它使程序員用Java同時開發客戶端和服務器端的代碼。GWT的編譯器會把用于開發客戶端的Java代碼轉化成Javascript和Html,而程序員不用關心這一轉換過程。這樣程序員就可以在自己喜歡的Java IDE里面開發自己的Ajax應用程序。
本文主要從以下幾個方面進行介紹:
1、 GWT特性簡介
2、 用GWT進行UI開發
3、 用Javascript開發Ajax應用簡介
4、 用GWT進行Ajax開發
GWT特性簡介
1.動態,可重用的UI組件
GWT提供的組件庫使用戶可以很容易的開發出漂亮的UI, 每個組件對應于GWT的一個類。在本文的第二部分會比較詳細的介紹GWT對UI的支持。
2.簡單的RPC調用
使用GWT,可以方便的實現客戶端和服務器端的通信,特別是使得異步通信變的非常簡單。在本文的后面部分將對利用GWT進行RPC調用進行詳細介紹。
3.更加方便的調試
由于在開發階段不需要生成HTML截面,用戶開發的代碼實際上是在JVM上運行的,這樣用戶就可以用傳統的調試Java代碼的方法對程序進行調試,從而加快了調試的速度,減少了軟件開發的時間。
4.瀏覽器兼容性
在大多數情況下,用GWT開發出來的程序會支持IE、Firefox、Mozilla、Safari和Opera,用戶在開發的時候不必擔心瀏覽器的兼容性問題。而瀏覽器的兼容性問題也是用直接用Javascript進行Ajax應用開發所面臨的一個另程序員棘手問題。
5.可擴展性
如果你覺得GWT提供的API不能滿足需求,你可以利用JSNI將Javascript語句直接嵌入至Java代碼中。?
用GWT進行UI開發
在本文的這一部分,我們將探討一下GWT對UI開發的支持。在GWT中,包含我們進行Web開發所需要的大部分組件,比如按紐(Button),文本框(Text box)等。圖一顯示了部分GWT所支持的UI組件。從圖片的顯示效果來看,利用GWT可以做出非常漂亮的UI。
圖一:GWT支持的部分UI組件
值得一提的是,在每個UI組件是必須放在一個稱之為面板(Panel)的控件里面。而面板具有不同的風格,這也決定了UI的風格。圖二顯示了GWT所支持的部分面板。
圖二:GWT支持的部分面板
在看過這些組件之后,我們接下來用一個實驗來講述怎樣把UI組件添加到頁面上。這個實驗的最終結果是一個登錄框界面。
在開始我們的實驗之前,我們需要準備一下GWT環境,首先要到Google的網站上去下載一個Windows版本的GWT,目前的版本號是1.0.21。然后要在機器上配置好JDK環境,具體的配置方法網絡上有大量文檔,這里就不再詳細介紹。由于我們這個實驗是在Eclipse開發環境下進行的,所以你還需要一個Eclipse的環境,可以到Eclipse的官方網站下載Eclipse的開發環境。接下來我們詳細介紹試驗過程。
1. 創建Eclipse工程
我們可以利用GWT自帶的一個批處理文件projectCreator.cmd來創建Eclipse的工程。如圖三所示,我們創建了一個名字為myProject的工程,存放在當前目錄的myProject子目錄下面,關于批處理文件projectCreator.cmd的詳細用法,請參見Google關于GWT的幫助文檔。
圖三:用GWT創建Eclipse工程
2. 創建GWT應用程序
在創建完Eclipse工程myProject之后,我們利用GWT自帶的另外一個批處理文件applicationCreator.cmd來創建一個GWT應用程序。圖四顯示了創建一個GWT應用的過程。大家注意到這個批處理文件接受一個名為 -eclipse 的參數,這個參數正是指定了一個Eclipse的工程,我們的例子中指定為我們剛創建好的Eclipse工程myProject。
圖四:創建GWT應用程序
3. 導入Eclipse工程
在創建完Eclipse工程和GWT應用程序框架后,我們接著將Eclipse工程導入到Eclipse開發環境當中以進行進一步的開發,具體的導入過程不再詳細介紹。導入后的工程結構如圖五所示。
圖五:導入Eclipse工程
6.添加UI組件
在導入工程后,我們會發現工程里面有一個名字為DemoClient的Java文件。這個文件是在運行applicationCreator.cmd批處理文件時創建的,我們現在需要做的工作就是在這個Java文件上加入自己需要的UI組件。我們在這個文件中加入了五個組件,分別是:兩個Label,一個Button,一個TextBox和一個PasswordTextBox。代碼列表Listing 1中列出了本程序的所有代碼。
Listing 1:Sample GWT Application
1 package com.sample.myProject.client;
2 import com.google.gwt.core.client.EntryPoint;
3 import com.google.gwt.user.client.ui.Button;
4 import com.google.gwt.user.client.ui.HorizontalPanel;
5 import com.google.gwt.user.client.ui.Label;
6 import com.google.gwt.user.client.ui.PasswordTextBox;
7 import com.google.gwt.user.client.ui.RootPanel;
8 import com.google.gwt.user.client.ui.TextBox;
/**
* This class is used to demostrate how to add widget onto the Web page
*/
9 public class DemoClient implements EntryPoint {
/**
* This is the entry point method, when the module is load, this method
* will be automatically invoked.
*/
10 public void onModuleLoad() {
11 Label labelName = new Label();
12 Label labelPassword = new Label();
13 TextBox tbName = new TextBox();
14 PasswordTextBox tbPassword = new PasswordTextBox();
15 Button button = new Button();
16
17 labelName.setText("Name: ");
18 labelPassword.setText("Password: ");
19 button.setText("submit");
20
21 HorizontalPanel hPanel = new HorizontalPanel();
22 HorizontalPanel hPanel2 = new HorizontalPanel();
23
24 hPanel.add(labelName);
25 hPanel.add(tbName);
26 hPanel2.add(labelPassword);
27 hPanel2.add(tbPassword);
28 RootPanel.get().add(hPanel);
29 RootPanel.get().add(hPanel2);
30 RootPanel.get().add(button);
31 }
32 }
|
接下來我們分析一下這些程序代碼,注意到類DemoClient繼承自EntryPoint,所有需要最終被翻譯成HTML頁面的類都必須繼承自EntryPoint,并且需要重寫onModuleLoad方法,這個方法會在模塊被裝載的時候自動調用。因此我們也就需要把我們的添加組件的代碼放到這個函數里面。
程序的11至15行分別創建了5個組件的實例。分別是兩個Label,一個Button,一個TextBox和一個PasswordTextBox。然后程序的17到19行分別設置了兩個Label組件以及一個Button組件的顯示內容。程序的21行和22行穿件兩個Panel對象,這兩個Panel對象的類型都是水平Panel對象。也就是說放在這種Panel里面的組件是被水平排列的。程序的24行到27行分別向這兩個Panel對象中加入TextBox組件和Label組件。在程序的最后,將剛才創建好的兩個Panel對象以及一個Button對象加到最外層的Panel當中。
7.編譯應用程序
在代碼開發完成后,我們可以雙擊工程里面的DemoClient-compile.cmd批處理文件來將我們開發出來Java文件編譯成Javascript和HTML。編譯后的文件將存放在工程的根目錄下面的www子目錄中。
8.運行程序
編譯好程序后,我們會發現生成了一個名字為DemoClient.html的HTML文件,雙擊這個文件,程序的運行結果如圖六所示:
圖六:程序運行結果
在這一部分,我們主要討論了如何將UI組件添加到Web頁面中,而這些組件如何與遠程服務器進行異步的交互將在下面的章節中進行討論。
用Javascript開發Ajax應用
為了使用戶能更清楚地理解用GWT開發Ajax應用與用傳統的Javascript開發Ajax應用的不同,文章的這一部分將簡要介紹如何利用Javascript對象進行Ajax應用的開發。
大家都知道Ajax技術中的一個核心對象是XMLHttpRequest對象,這個對象支持異步請求,所謂異步請求既是當客戶端發送一個請求到服務器的時候,客戶端不必一直等待服務器的響應。這樣就不會造成整個頁面的刷新,給用戶帶來更好的體驗。而當服務器端響應返回時,客戶端利用一個Javascript函數對返回值進行處理,以更新頁面上的部分元素的值。
由于在IE和其他瀏覽器上聲稱XMLHttpRequest對象的方法不一樣,所以我們用Javascript創建XMLHttpRequest對象的時候必須區分不同的瀏覽器。創建一個XMLHttpRequest對象的方法如清單2 中的代碼所示。
清單2:創建XMLHttpRequest對象
1 function createObject(){
2 try {
3 xmlHttpRequest = new ActiveXObject("Msxml2.XMLHTTP");
4 } catch (e1) {
5 try {
6 xmlHttpRequest = new ActiveXObject("Microsoft.XMLHTTP");
7 } catch (e2) {
8 xmlHttpRequest = false;
9 }
10 }
11 if (!xmlHttpRequest) {
12 xmlHttpRequest = new XMLHttpRequest();
13 }
14 return xmlHttpRequest;
16 }
|
在創建好XMLHttpRequest對象之后,來看一下如何與server端進行異步的交互。清單3中列出了與服務器段進行交互的代碼
清單3:與服務器端進行交互
1 function ajaxSample() {
2 var xmlHttpRequest = createObject();
3 var url = "/sampleServlet?userName=Jason";
4 xmlHttpRequest.open("GET", url, true);
5 xmlHttpRequest.onreadystatechange = updatePage;
6 xmlHttpRequest.send(null);
7 }
|
這段代碼演示了如何與服務器端進行交互的過程。程序的第2行首先獲得一個XMLHttpRequest對象,程序的第三行指定了服務器端響應客戶端的請求的地址。程序的第4行利用XMLHttpRequest對象打開一個連接,第一個參數指定用GET方法傳遞參數,第二個參數指定了接受請求的URL,在我們的例子中是一個Servlet,最后一個參數設置成true意味著將要發出的請求是一個異步的請求。程序的第5行指定了回調函數,也就是當服務器端返回結果后執行哪個Javascript函數。
這一部分對如何利用Javascript進行Ajax開發作了一個簡要的介紹,下面我們將詳細介紹如何利用GWT進行Ajax應用的開發。
用GWT進行Ajax開發
在文章的第二部分我們已經掌握了如何利用GWT創建一個工程并在工程里添加一個GWT應用程序,同樣我們這一部分也需要創建一個GWT的工程,并添加一個GWT應用程序,由于默認創建的GWT程序不含有Server端的示例代碼,我們必須手工加進去。我們創建好的工程GWTSample如圖七所示。我們將要介紹的實例主要功能是采用異步通信的方式從服務器端取出一個字符串顯示在HTML頁面上。這個例子雖然簡單,但卻包含了如何利用GWT進行Ajax開發的主要流程。
圖七:GWTSample工程結構
大家注意到我們這個工程里面有個com.sample.myProject.server包,這個包里面包含有運行在服務器端的代碼。我們從客戶端發送的請求也是發送到這個包里面的一個Servlet上去的。
為了進行異步調用,在Client端必須定義一個繼承自接口RemoteService的接口,在我們的這個例子中,我們定義了接口SampleService,SampleService的代碼如清單4所示。
1 package com.sample.myProject.client;
2 import com.google.gwt.user.client.rpc.RemoteService;
3 public interface SampleService extends RemoteService{
4 //The implementation of this method is used to return a string
5 String getString();
6 }
|
大家注意到這個接口繼承自RemoteService,并且聲明了一個方法getString();,這個方法會在服務器端的代碼中實現。當然,大家可能現在已經意識到,這里聲明的方法就是我們采用異步調用方式所能夠調用的方法。
在聲明完這個接口之后,我們還必須聲明另外一個異步調用接口,在我們的例子中是接口SampleServiceAsync,這個接口里聲明的方法名稱必須與SampleService里面的相同,但是多個一個類型為AsyncCallback的參數,接口SampleServiceAsync的代碼清單如清單5所示:
清單5:SampleServiceAsync代碼清單
1 package com.sample.myProject.client;
2 import com.google.gwt.user.client.rpc.AsyncCallback;
3 public interface SampleServiceAsync {
4 void getString(AsyncCallback callback);
5 }
|
在客戶端定義完接口之后,我們必須在服務器端實現這個接口,在我們的例子中,類SampleServiceImpl實現了接口SampleService,同時你也會注意到SampleServiceImpl也繼承了類RemoteServiceServlet,而RemoteServiceServlet是HttpServlet的一個子類,這樣我們的客戶端的請求就可以提交到Servlet SampleServiceImpl上面。類SampleServiceImpl的代碼清單如清單6所示:
清單6:SampleServiceImpl代碼清單
1 package com.sample.myProject.server;
2 import com.google.gwt.user.server.rpc.RemoteServiceServlet;
3 import com.sample.myProject.client.SampleService;
4 public class SampleServiceImpl extends RemoteServiceServlet implements
5 SampleService {
6 public String getString() {
7 return "This string is from server";
8 }
9 }
|
最后我們來看一下類DemoClient,這個類和我們在第二部分創建的工程myProject中的類DemoClient是同一個類型的。只不過在我們這個工程中我們使用它來進行向server端的異步調用。清單7列出了類DemoClient的代碼。
清單7:DemoClient代碼清單
1 package com.sample.myProject.client;
2 import com.google.gwt.core.client.EntryPoint;
3 import com.google.gwt.core.client.GWT;
4 import com.google.gwt.user.client.rpc.AsyncCallback;
5 import com.google.gwt.user.client.rpc.ServiceDefTarget;
6 import com.google.gwt.user.client.ui.Button;
7 import com.google.gwt.user.client.ui.ClickListener;
8 import com.google.gwt.user.client.ui.Label;
9 import com.google.gwt.user.client.ui.RootPanel;
10 import com.google.gwt.user.client.ui.Widget;
/**
* This class is used to demostrate hwo to
* interact with the server client in asynchronized
* way
*/
11 public class DemoClient implements EntryPoint {
12 public void onModuleLoad() {
13 final SampleServiceAsync sampleService = (SampleServiceAsync)
14 GWT.create(SampleService.class);
15 ServiceDefTarget target = (ServiceDefTarget)sampleService;
16 String staticResponseURL = GWT.getModuleBaseURL();
17 staticResponseURL += "/getStringService";
18 target.setServiceEntryPoint(staticResponseURL);
19
20 final Label label = new Label();
21 final Button button = new Button("Get String");
22 button.addClickListener(new ClickListener() {
23 public void onClick(Widget sender) {
24 sampleService.getString(new AsyncCallback() {
25 public void onSuccess(Object result) {
26 label.setText((String) result);
27 }
28 public void onFailure(Throwable caught) {
29 label.setText(caught.getMessage());
30 }
31 });
32 }
33 });
34 RootPanel.get("1").add(button);
35 RootPanel.get("2").add(label);
36 }
37}
|
代碼的第13行得到了一個實現了接口SampleServiceAsync的類的實例。第15行創建了一個ServiceDefTarget對象的一個實例,通過這個對象可以設置請求的目的地。程序的第18行設置了請求的目的地的URL,在我們的例子中是"/getStringService",這個URL會在web.xml文件中被mapping到servlet SampleServiceImpl上。程序的22行到33行為我們添加的button設置了單擊響應事件。在單擊響應事件中調用sampleService的getString(AsyncCallback callback);方法。這個方法是用來進行異步的遠程過程調用的(RPC).并且在實現接口AsyncCallback的代碼中指定了回調函數,當遠程過程調用成功后就執行onSuccess(Object result)函數,其中result中存放有從服務器端返回的結果.。在遠程工程調用失敗后就執行onFailure(Throwable caught)函數。程序的最后把Button組件和Label組件加到panel中。
現在我們已經完成了程序的開發,圖八顯示了我們程序的運行結果,在點擊Button后,右邊回打出一句話來,重要的是這句話是以異步的方式從服務器端取得的,不需要進行頁面的刷新,怎么樣,現在是不是也想用GWT進行Ajax應用開發了呢?
圖八:RPC調用示例
總結
本文主要對用 GWT 進行 Ajax 開發進行了比較詳細的介紹,并通過與傳統的Ajax開發方式進行比較,使讀者能更清楚地理解它們之間的區別,最后我們可以看出用GWT進行Ajax開發可以使得程序員免受調試Javascript之苦,并且GWT自動處理了瀏覽器之間的兼容性問題,這些都會使得開發更加容易,快捷。因此,用GWT進行Ajax開發是一種比較好的方式。希望本文能為讀者學習GWT進行Ajax的開發有所幫助。
下載
GWTSample_Project.rar |
8KB |
HTTP
|