??? 老早xc就在我耳邊絮絮叨叨gwt的好處呢,可是由于受google maps在我瀏覽器里總無(wú)法正常顯示的影響(莫非是人品問(wèn)題?), 我差點(diǎn)就與它擦肩而過(guò). 還是因?yàn)镃ain 某天在群里述說(shuō)自己因?yàn)闆](méi)有刪除gwt-user.jar中javax部分所受的痛苦折磨,才讓我決心一試gwt, 呵呵, 老大都出手了,小弟怎能落后呢?
??? 關(guān)于gwt的詳細(xì)內(nèi)容我就不多介紹了,具體可以參見(jiàn)我blog上轉(zhuǎn)載的兩篇文章Google Web Toolkit 入門(mén)和gwt study .這里就主要結(jié)合我自己的試用經(jīng)歷講下吧.
??? ajax現(xiàn)在的應(yīng)用也很廣泛了,其中可能用的最多,也幾乎會(huì)在每本介紹ajax的書(shū)上出現(xiàn)的案例,大概就是關(guān)于用戶注冊(cè)時(shí)對(duì)用戶名的檢驗(yàn).因此我也選中用gwt完成類似的功能.考慮到demo的簡(jiǎn)易性,具體的功能縮減如下:
????? 在輸入框中輸入用戶名后,將鼠標(biāo)點(diǎn)離輸入框, 程序?qū)⒄{(diào)用后臺(tái)servlet自動(dòng)檢驗(yàn)輸入信息.如果輸入內(nèi)容為空,則提示錯(cuò)誤信息;如果輸入信息為dyerac,則提示該用戶已注冊(cè)(本來(lái)應(yīng)該是先檢驗(yàn)數(shù)據(jù)庫(kù)看用戶名是否已經(jīng)存在,為了簡(jiǎn)便我省去了這一步,直接用dyerac替代);如果都不是,則顯示出歡迎信息.
???? 同樣,你也可以直接點(diǎn)擊"test ajax"按鍵手工進(jìn)行檢驗(yàn),檢驗(yàn)原則和前面相同
????????
????? 
???? 好咯,下面就讓我們一步步來(lái)吧.
??? 1.下載gwt
?? 從 http://code.google.com/webtoolkit/ ?下載GWT的最新版本,將下載的壓縮文件解壓縮到D:/GWT目錄下。本書(shū)中的后續(xù)內(nèi)容中將使用%GWT_HOME%變量來(lái)引用這個(gè)目錄。點(diǎn)開(kāi)目錄,會(huì)發(fā)現(xiàn)目錄中含有三個(gè)cmd文件和兩個(gè)jar文件:
?????????????? 
??? ?分別將他們加入classpath和path中
?
??? 2.創(chuàng)建工程:
??? 創(chuàng)建目錄%GWT_HOME%/gwt-workspace/gwtTrial, 通過(guò)命令行進(jìn)入到該目錄下,輸入命令projectCreator????? ?-eclipse gwtTrial? 創(chuàng)建eclipse同名工程. 然后用eclipse將該工程導(dǎo)入.
??? 在eclipse下給項(xiàng)目添加兩個(gè)包: com.dyerac.client,? com.dyerac.server.注意,gwt會(huì)把你的java代碼翻譯成js代碼,由于gwt的強(qiáng)制規(guī)定,所以需要翻譯的java代碼都必須放到以client結(jié)尾的包中,同時(shí),為了程序的易讀性,gwt也建議把servlet放在server結(jié)尾的包中.
??? 3.代碼開(kāi)發(fā)
??? gwt最方便的地方之一在于,你完全不用開(kāi)發(fā)js代碼, gwt仿照awt開(kāi)發(fā)了自己的一套組件庫(kù),你可以通過(guò)這套組件庫(kù)像開(kāi)發(fā)swing一樣開(kāi)發(fā)自己的web頁(yè)面,然后調(diào)用gwt的工具將它翻譯成js代碼.
?? gwt另外一個(gè)好處在于在Ajax應(yīng)用中使用RPC消除了顯式地處理XMLHttpRequest和相關(guān)聯(lián)的服務(wù)器返回值的需要,因?yàn)镚WT對(duì)象會(huì)為你處理這些通訊工作。gwt采取了一種和老式ejb很像的方式來(lái)處理客戶端和服務(wù)端的異步通訊, 當(dāng)你開(kāi)發(fā)一個(gè)需要被客戶端調(diào)用的服務(wù)時(shí),你同時(shí)還需要開(kāi)發(fā)兩個(gè)接口:
package
?com.dyerac.client;
import
?com.google.gwt.user.client.rpc.RemoteService;


public
?
interface
?InputService?
extends
?RemoteService
{
???
public
?String?checkInput(String?req);
}
服務(wù)接口必須擴(kuò)展GWT接口RemoteService。它只定義了一個(gè)方法 checkInput(String req);
你還需要定義一個(gè)接口,客戶端或最終被下載的JavaScript代碼將會(huì)用它調(diào)用服務(wù)方法。GWT使用了一種回調(diào)設(shè)計(jì)模式,當(dāng)我給出客戶端代碼的時(shí)候會(huì)再來(lái)描述它(請(qǐng)看MyInput.java)。
package
?com.dyerac.client;

import
?com.google.gwt.user.client.rpc.AsyncCallback;


public
?
interface
?InputServiceAsync
{
????
public
?
void
?checkInput(String?req,?AsyncCallback?callable);
}
要使一個(gè)服務(wù)在GWT中可用必須遵守命名約定;在服務(wù)接口名(InputService)后增加Async后綴。AsyncCallback對(duì)象是GWT API的一部分,它的目的是為客戶端處理服務(wù)響應(yīng)。不管怎樣,等你看了這段代碼應(yīng)用的地方后會(huì)對(duì)這一行為有更清晰的了解。這些對(duì)象定義都位于用于生成客戶端JavaScript的Java代碼中。
最后,你需要定義一個(gè)Java類來(lái)實(shí)現(xiàn)遠(yuǎn)程服務(wù)接口。這個(gè)類將會(huì)存在于你的Ajax應(yīng)用程序的服務(wù)器端。
package?com.dyerac.server;

import?com.dyerac.client.InputService;
import?com.google.gwt.user.server.rpc.RemoteServiceServlet;

public?class?InputServiceImpl?extends?RemoteServiceServlet?implements

????????InputService?
{


????public?String?checkInput(String?req)?
{
????????//?TODO?Auto-generated?method?stub
????????String?buf;
????????if?(req.equalsIgnoreCase("dyerac"))
????????????buf?=?"sorry,?this?id?has?been?registered";
????????else
????????????buf?=?"Hello,?"?+?req;
????????return?buf;
????}

}

該類必須擴(kuò)展RemoteServiceServlet,這是一個(gè)GWT API對(duì)象,它本身擴(kuò)展了javax.servlet.http.HttpServlet。也就是說(shuō),該類和它實(shí)現(xiàn)的接口需要被部署到你的servlet容器.(注: 在tomcat下部署的時(shí)候,需要?jiǎng)h除gwt-user.jar中的javax部分)
現(xiàn)在,服務(wù)已經(jīng)定義完畢了,只剩下對(duì)應(yīng)頁(yè)面的java類沒(méi)有開(kāi)發(fā):
package?com.dyerac.client;

import?com.google.gwt.core.client.EntryPoint;
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.FocusListener;
import?com.google.gwt.user.client.ui.Grid;
import?com.google.gwt.user.client.ui.Label;
import?com.google.gwt.user.client.ui.RootPanel;
import?com.google.gwt.user.client.ui.TextBox;
import?com.google.gwt.user.client.ui.Widget;


public?class?MyInput?implements?EntryPoint,?ClickListener,?FocusListener?
{

????Button?submit?=?new?Button("test?ajax");


????/**?*//**
?????*?輸入框
?????*/
????TextBox?input?=?new?TextBox();


????/**?*//**
?????*?錯(cuò)誤提示標(biāo)簽
?????*/
????Label?message?=?new?Label();


????/**?*//**
?????*?一個(gè)Grid對(duì)象;實(shí)際上,是一個(gè)HTML?table
?????*/
????Grid?grid?=?new?Grid(2,?2);


????/**?*//**
?????*?判斷是否是第一次得到焦點(diǎn).只有第一次得到焦點(diǎn)后,才會(huì)在失去焦點(diǎn)時(shí)進(jìn)行校驗(yàn)
?????*/
????boolean?isFirstFocused?=?false;


????/**//*
?????*?當(dāng)瀏覽器載入應(yīng)用程序時(shí)本方法被調(diào)用。?本方法添加標(biāo)簽和文本框,以及一個(gè)?按鈕到頁(yè)面上,并為他們注冊(cè)監(jiān)聽(tīng)器
?????*/

????public?void?onModuleLoad()?
{
????????//?TODO?Auto-generated?method?stub
????????grid.setWidget(0,?0,?input);
????????grid.setWidget(0,?1,?message);
????????grid.setWidget(1,?0,?submit);
????????//?添加點(diǎn)擊監(jiān)聽(tīng)器
????????submit.addClickListener(this);
????????//?添加得失焦點(diǎn)監(jiān)聽(tīng)器
????????input.addFocusListener(this);
????????RootPanel.get().add(grid);
????}


????public?void?onClick(Widget?sender)?
{
????????//?TODO?Auto-generated?method?stub
????????check();
????}


????/**?*//**
?????*?校驗(yàn)輸入框中內(nèi)容,只有內(nèi)容非空且不為dyerac時(shí),?才會(huì)顯示正確歡迎信息
?????*/

????public?void?check()?
{
????????//?為服務(wù)器端服務(wù)創(chuàng)建一個(gè)客戶端存根的實(shí)例
????????InputServiceAsync?inputService?=?(InputServiceAsync)?GWT
????????????????.create(InputService.class);
????????ServiceDefTarget?target?=?(ServiceDefTarget)?inputService;
????????//?下面的"/inputservice"表示之前開(kāi)發(fā)的InputServiceImpl在web應(yīng)用中對(duì)應(yīng)的servlet映射
????????//?即web.xml配置的內(nèi)容.
????????//?在開(kāi)發(fā)中,也可以在MyInput.gwt.xml中進(jìn)行配置
????????target.setServiceEntryPoint("/inputservice");


????????AsyncCallback?callback?=?new?AsyncCallback()?
{

????????????public?void?onSuccess(Object?result)?
{
????????????????String?s?=?(String)?result;

????????????????if?(s.startsWith("H"))?
{
????????????????????if?(message.getStyleName().equalsIgnoreCase("warning"))
????????????????????????message.removeStyleName("WARNING");
????????????????????message.setText(s);

????????????????}?else?
{
????????????????????message.setStyleName("WARNING");
????????????????????message.setText(s);
????????????????}
????????????}


????????????public?void?onFailure(Throwable?caught)?
{
????????????????message.setStyleName("WARNING");
????????????????message.setText("found?a?error:?"?+?caught.toString());
????????????}
????????};
????????String?tmp?=?input.getText();
????????//?先檢驗(yàn)輸入是否為空

????????if?(tmp.length()?<?1)?
{
????????????message.setStyleName("WARNING");
????????????message.setText("A?textbox?had?invalid?content?such?"
????????????????????+?"as?a?blank?entry.");

????????}?else?
{
????????????//?調(diào)用服務(wù)器端方法
????????????inputService.checkInput(tmp,?callback);
????????}
????}


????public?void?onFocus(Widget?sender)?
{
????????//?TODO?Auto-generated?method?stub
????????if?(isFirstFocused?==?false)
????????????isFirstFocused?=?true;
????????message.setText("");
????}


????public?void?onLostFocus(Widget?sender)?
{
????????//?TODO?Auto-generated?method?stub

????????if?(isFirstFocused?==?true)?
{
????????????check();
????????}
????}
}

?
怎么樣,是不是像開(kāi)發(fā)swing一樣簡(jiǎn)單? 再說(shuō)明一下,本類中的onModuleLoad()方法就如同普通java類中的main方法,是整個(gè)程序的切入口.
代碼開(kāi)發(fā)完后,就要調(diào)用gwt工具進(jìn)行編譯(把java代碼翻譯成js代碼),這里,只有MyInput.java是針對(duì)頁(yè)面開(kāi)發(fā)的,所以我們只需要翻譯它.還是在命令行下進(jìn)入%GWT_HOME%/gwt-workspace/gwtTrial, 輸入命令applicationCreator -eclipse gwtTrial -ignore com.dyerac.client.MyInput (-ignore表示該類已存在,不需要重新創(chuàng)建)
隨后刷新eclipse下的工程,發(fā)現(xiàn)結(jié)構(gòu)如下:
注:下圖中DialogBoxExample有關(guān)的內(nèi)容是我另外開(kāi)發(fā)

??? MyInput-shell.cmd是在宿主模式(Hosted Mode)下啟動(dòng)程序,宿主模式是指我們和沒(méi)有轉(zhuǎn)換為Ajax應(yīng)用的GWT應(yīng)用交互的狀態(tài)。當(dāng)我們開(kāi)發(fā)和調(diào)試時(shí),我們就一直處在宿主模式下。在這種情況下,Java虛擬機(jī)使用GWT內(nèi)置的瀏覽器運(yùn)行GWT應(yīng)用編譯后的class內(nèi)容,因此能夠提供"編碼、測(cè)試、調(diào)試"過(guò)程的最佳速度。
??? 對(duì)應(yīng)的還有Web模式,是指已經(jīng)成功轉(zhuǎn)化為Ajax應(yīng)用的狀態(tài),這種狀態(tài)下,我們已經(jīng)開(kāi)始通過(guò)Web方式來(lái)訪問(wèn)Ajax應(yīng)用了。在Web模式下運(yùn)行時(shí),不再需要GWT工具包或者JVM的支持。啟動(dòng)web模式需要用MyInput-compile.cmd先編譯整個(gè)應(yīng)用.
?
?? 另外,我們還需要修改一下MyInput.gwt.xml:
?? 添加<servlet path="/inputservice" class="com.dyerac.server.InputServiceImpl" />
<module>

????<!--?Inherit?the?core?Web?Toolkit?stuff.??????????????????-->
????<inherits?name='com.google.gwt.user.User'/>

????<!--?Specify?the?app?entry?point?class.???????????????????-->
????<entry-point?class='com.dyerac.client.MyInput'/>
????
????<servlet?path="/inputservice"?class="com.dyerac.server.InputServiceImpl"?/>?
</module>

最后,為了調(diào)試方便,我們可以直接點(diǎn)擊MyInput-shell.cmd,在宿主模式下運(yùn)行程序

4.一些困惑:?
?? 雖然gwt很是方便,但我覺(jué)得它自身就是一個(gè)完整的體系了,因此如何與現(xiàn)有框架進(jìn)行整合還是個(gè)問(wèn)題.比如,如何與jsf整合呢?還望各位高手支招
?? 而且gwt還是缺乏一款強(qiáng)大的ide支持,如果實(shí)現(xiàn)頁(yè)面的wysiwyg就方便多了.另外由命令行編譯也不是很方便
呵呵,期待gwt下一個(gè)版本^
?????
?