一、 引言
如果你是一個Java軟體和Ajax開發人員,那麼,Google Web Toolkit(GWT)應該已經引起你的關注。
Google公司已經於2006年5月在Apache許可協議下發布了這種免費的開發套件。GWT的設計目的是為了簡化用Java語言開發Ajax應用程式。Google初始發行的beta版本可以適用於Windows和Linux平台,並確保稍後要發行一個Mac OS X版本。
本文將探討在Mac OS X上使用GWT和熟悉的Java工具,例如Apache Ant,Tomcat 5.0 servlet容器和IntelliJ IDEA整合式開發環境開發一個簡單的Ajax應用程式的完整過程。
注 本文假定讀者具有一定的Java和Ant使用基礎。
二、 與GWT一起使用Ant
我下載的是GWT的Linux beta版本,並選用Java開發應用程式,然後使用一個Ant構建檔案進行編譯,最後在一個Tomcat 5.0執行個體上發布該應用程式。注意,這個Ant檔案啟動並執行是GWT Java-to-JavaScript編譯器。其實,這個“編譯器”只是一個執行一個GWT Java類的命令列指令碼,該Java類負責為應用程式編寫JavaScript。
使用GWT beta包括兩種開發方式:主機方式和web方式。
主機方式是使用一個嵌入式的GWT瀏覽器和中間開發步驟;在這個方式中,你的編譯代碼繼續運行於一個Java虛擬機器(JVM)中。然而,主機方式無法應用於我們這些使用Linux版本作業系統的Mac OS X使用者。只有Google發行了一個Mac OS X版本,我們才可以使用主機方式。
三、 不同風格的Web開發
在建立遠端程序呼叫(RPC)服務的同時,本文將詳細討論一些典型的GWT開發人員可能面對的web開發有關的任務。RPC是一個軟體模型的一部分,主要為使用面向服務的架構(SOA)的應用程式而設計。這些開發工作單位包括:
· 使用一個構建檔案(構建運行GWT編譯器,然後發布編譯器的輸出,並且把你的伺服器端的Java類檔案發布到一個servlet容器,例如Tomcat,Jetty或Resin)來自動化開發和發布步驟。
· 使用Firefox的DOM Inspector來觀察由該GWT應用程式產生的HTML。
· 重新設定頁面中的各組件而不必存取內在的HTML(既然你在使用GWT的Java API)。
· 確保HTML是有效標記,例如,你的組織可能需要基於一個特別的XHTML文件類型。
四、 服務功能
首先,我將簡短描述本文應用程式範例要建立的服務,設計這個樣本是為了展示GWT使用的模型。
該應用程式在瀏覽器中顯示一個表單,要求使用者輸入他們的姓名、年齡和原籍國家。當使用者通過點按按鈕提交表單時,該應用程式在一個文本域中顯示一個伺服器響應,而不必初始化一個頁面重新整理。圖1顯示了在Safari瀏覽器中該應用程式看上去的樣子。
例如,當使用者保留一個文字框為空白而點擊OK,Submit按鈕時將顯示出圖2所示結果。
五、 巧妙的服務機制
在Ajax應用程式中使用RPC可以消除顯式地處理XMLHttpRequest和相關的伺服器傳回值的必要性,因為GWT對象能夠為你處理複雜任務。
你的應用程式定義的每個服務都要求實現兩個Java介面和一個Java類。為了編譯這些類,你必須確保gwt-user.jar庫位於你的classpath(由一個Ant檔案入口負責實現這項任務)中。下列程式碼範例展示了定義我們的服務的Java介面。
package com.parkerriver.gwt.testapp.client; import com.google.gwt.user.client.rpc.RemoteService; public interface ShowRespService extends RemoteService{ String displayResponse(String req); } |
這個服務介面要求擴充GWT介面RemoteService。它定義了單個方法displayResponse()。
另外,你還必須定義一個用戶端(或使用最終下載的JavaScript代碼)用於調用這個服務方法的介面。當我顯示用戶端代碼時(請參考MyForm.java),該GWT使用一個我描述的回調設計模式。
package com.parkerriver.gwt.testapp.client; import com.google.gwt.user.client.rpc.AsyncCallback; public interface ShowRespServiceAsync { public void displayResponse(String s,AsyncCallback callback); } |
這個AsyncCallback對象負責(作為GWT API的一部分)為用戶端處理服務響應。
六、 一個Servlet
最後,你必須定義一個實現遠程服務介面的Java類。這個類將位於你的Ajax應用程式的伺服器端。
package com.parkerriver.gwt.testapp.server; import com.parkerriver.gwt.testapp.client.ShowRespService; import com.google.gwt.user.server.rpc.RemoteServiceServlet; import java.util.Date; public class ShowRespServiceImpl extends RemoteServiceServlet implements ShowRespService { public String displayResponse(String req) { if(req.length() < 1) { throw new IllegalArgumentException( "Blank submissions from the client are invalid."); } StringBuffer buf = new StringBuffer("Your submission: "); Date date = new Date(); String serverInfo = this.getServletContext().getServerInfo(); buf.append(req); buf.append("\n"); buf.append("Server response: "); buf.append(date.toString()); buf.append("\n"); buf.append(serverInfo); return buf.toString(); } } |
這個類必須繼承RemoteServiceServlet(這是本身繼承自javax.servlet.http.HttpServlet的一個GWT API對象)。也就是說,這個類及其實現的介面必須被發布到你的servlet容器中。
七、 步驟
現在,既然定義了服務,那麼讓我們來回顧一個這個應用程式的目錄結構。GWT包括一個命令列指令碼applicationCreator,它能夠為你產生一個架構性的工程目錄結構。在你解壓下載的GWT後,你會在頂級目錄下發現該applicationCreator。我使用下列命令列作為開始:
| applicationCreator -out /Users/bruceperry/1gwt/secondapp/ com.parkerriver.gwt.testapp.client.MyForm |
圖3展示了該目錄看上去的樣子。
applicationCreator產生./src目錄以及MyForm-compile和MyForm-shell指令碼。我的Ant檔案執行的是MyForm-compile;另一個指令碼將在GWT模式下啟動主機方式。這個./src目錄中包括了嵌套的目錄以匹配你的包名,如圖4所示。
MyForm.gwt.xml檔案是一個產生的設定檔,它其實是GWT調用的一個“模組”。它指定描述你的應用程式“進入點”的Java類(這是一個類似於一個包含一個main()方法的Java類)。
<module> <!—繼承核心Web Toolkit。--> <inherits name='com.google.gwt.user.User'/> <!--指定應用程式進入點類。--> <entry-point class='com.parkerriver.gwt.testapp.client.MyForm'/> </module> |
其它的檔案或目錄,包括./classes、./WEB-INF和./gwtproj.ipr,都是一個IntelliJ Web應用程式工程的必要組成部分;因此,你不必特別注意它們。
另外,直到產生你的應用程式代碼的GWT編譯器時,./www目錄才出現(除非你自己建立它)。我的工程使用了Ant檔案gwtproj.xml,還有定義在gwtproj.properties中的屬性。在我向你展示Ant構建檔案前,我們先來看一下描述應用程式進入點的MyForm.java類。
八、 進入點
這個MyForm.java類實現了GWT API介面EntryPoint;因此,該類必須實現onModuleLoad()方法,當瀏覽器載入你的Ajax應用程式時此方法為瀏覽器的JavaScript引擎所調用。
也就是說,GWT編譯器把這個類編譯成JavaScript代碼。MyForm.java類為瀏覽器視圖建立表單widget。該類還決定了點擊OK和Submit按鈕時使用者的響應。代碼中的注釋已經作了詳細的描述,所以在此不再多言。
注意,這個類中的大部分代碼是處理GWT API。有意思的是,如果你必須實現JavaScript DOM編程(就象在showRpcStatus()方法中所展示的),那麼你可以用Java來實現com.google.gwt.user.client.DOM類。
九、 構建檔案
下面是Ant構建檔案的主要功能;這個構建檔案:
1. 把Java檔案編譯到工程目錄的./classes目錄下。
2. 執行GWT編譯指令碼(在這個例子中是MyForm-compile)。
3. 把在./www目錄下產生的結果代碼移動到一個較大的已經發布到Tomcat上的web應用程式。
4. 把編譯的Java servlet及相關介面(ShowRespService)複製到同一個web應用程式下。
注意,這裡的兩個目標:編譯Java類和初始化到JavaScript的轉換有可能使整個構建過程失敗,如果期間發生任何錯誤的話。
十、 Ant XML
下面是gwtpoj.properties檔案包含的內容(省略了其它的內容):
web.deploy.location=/users/bruceperry/parkerriver/gwt web.classes.location=/users/bruceperry/parkerriver/WEB-INF/classes |
下列XML描述了剛才的Ant檔案的主要功能:
<?xml version="1.0" encoding="UTF-8"?> <project name="gwtproj" default="all"> <property file="gwtproj.properties"/>
<!—工程的頂級目錄與ant檔案存在的位置--> <dirname property="module.gwtproj.basedir" file="${ant.file}"/>
<!--目錄在頂級目錄內的./classes--> <property name="gwtproj.output.dir" value= "${module.gwtproj.basedir}/classes"/>
<!--這個目標調用MyForm-compile以建立./www目錄下的所有的內容--> <target name="gwt-compile" depends= "compile.production.classes" description="use gwt's compiler"> <delete> <fileset dir="${web.deploy.location}" includes="**/*"/> </delete> <exec executable= "${module.gwtproj.basedir}/MyForm-compile" failonerror="true"/> <copy todir="${web.deploy.location}"> <fileset dir= "${module.gwtproj.basedir}/www"> </fileset> </copy> </target> <target name="compile.production.classes" description= "Compile the gwtproj production classes"> <mkdir dir="${gwtproj.output.dir}"/> <javac destdir="${gwtproj.output.dir}" debug= "on" failonerror="true" nowarn= "off" memoryMaximumSize="128m" fork= "true" executable="${module.jdk.home.gwtproj}/bin/javac"> <classpath refid="gwtproj.module.classpath"/> <src refid="gwtproj.module.sourcepath"/> </javac> </target>
<!--把Java servlet類複製到web應用程式--> <target name="deploy.classes" depends="gwt-compile" description="copy classes to web directory"> <copy todir="${web.classes.location}"> <fileset dir="${gwtproj.output.dir}"> </fileset> </copy> </target>
<target name="all" depends="deploy.classes" description="build all"/> </project> |
你可以從IDE(在IntelliJ中)中或在包含該構建檔案的目錄下使用下列命令列來運行這個Ant檔案:
ant -buildfile gwtproj.xml
大部分情況下,在修改應用程式和運行Ant後,你都可以通過重載瀏覽器頁面而在瀏覽器中看到這些變化。
十一、 安裝
注意,在安裝時,你必須把gwt-user.jar庫添加到你的web應用程式的/WEB-INF/lib目錄下。
我把建立的JAR檔案,還有javax包(gwt-user-deploy.jar)都添加到/WEB-INF/lib目錄下。這是因為Tomcat不會載入單個的web應用程式的庫檔案,如果它已經包含servlet API類的話。
十二、 痛點
applicationCreator還會為你建立Ajax應用程式的HTML前端,在這個例子中是MyForm.html。
如果你的應用程式的HTM必須滿足一種標準(例如XHTML Transitional 或Strict),那麼該怎麼辦呢?對於XHTML transitional,我首先把要求的DOCTYPE添加到MyForm.html的頂部以及該html標籤的相關屬性中:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/2000/REC-xhtml1-20000126/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> |
然後,我把MyForm.html上傳到WWW協會的HTM驗證程式(位於http://validator.w3.org/)。
在運行該驗證程式後,我略微修改了一下該HTML,例如適當關閉meta標籤並且把一個type="text/javascript"添加到指令碼標籤。
然而,如果你想滿足XHTML Strict標準,那麼還需要進行更為複雜的修改。例如,W3C的驗證程式將把iframe標籤顯示為“undefined element”,而這個標籤是GWT的曆史支援功能(提供與一個瀏覽器back按鈕相同的功能)必需的。XHTML Strict中已經刪除了iframe元素。
這個對你來說可能不是個問題(可能會在GWT的未來版本中得到解決,還有任何其它明顯的問題);然而,你還可以實現其它可選策略,例如擴充GWT的類並建立你自己的相容widget等。
十三、 位置對齊問題
一個在web開發中普通存在的問題就是應用程式的可視化設計問題。工程的設計者可能想使頁面看上去如其在Adobe Illustrator建立一樣的效果,對不對?
儘管當你開發一個複雜的Ajax工程時你可能無法實現這種理想的視覺效果;但是,你至少可以使用Firefox的DOM Inspector來觀察你的Java類最終產生的HTML。然後,再從這裡進行修改。
例如,轉到Firefox的“Tools=>DOM Inspector”功能表項目(見圖5)。
圖5:使用DOM Inspector觀察後台實現內容。 |
上圖顯示了,你在Java代碼中使用的com.google.gwt.user.client.ui.Grid對象被實現為一個HTML table標籤。這個table中的包含OK,Submit按鈕的TD標籤與一個style屬性(其值為"verticle-align:top")相關聯。
下面是在MyForm.java類中相關的初始化適當格式的Java代碼:
//設定OK按鈕儲存格的垂直位置 grid.getCellFormatter().setVerticalAlignment(3,0, HasVerticalAlignment.ALIGN_TOP); |
如果在代碼中不進行這個調用,那麼該按鈕可能會在文本域的中間部分上下浮動。
十四、 總結
Google公司發布的Google Web Toolkit(GWT)已經引起業界的普遍關注。既然GWT的設計目的是為了簡化用Java語言開發Ajax應用程式,而Ajax是Web 2.0時代的技術基礎,所以GWT發行所引起的轟動應該在意料之中。
本文僅給出使用GWT和熟悉的Java工具開發一個簡單的Ajax應用程式的樣本,而有關GWT的更多的探究剛剛開始,還依賴於廣大讀者的努力。