摘要
Struts是一個優秀的Java Web開發框架。Struts是Apache項目之一,現在Struts已經在Java開發團體中得到了廣泛的支持。在這篇文章中Julien Mercay 和Gilbert Bouzeid將向我們介紹Struts的處理流程、描繪Struts框架,最後提出了Model 2x的設計方法。Model 2x用XML/XSLT替換掉了原來的表現層中的JSP頁面。Model 2x通過這種方式更加明晰地劃分了業務邏輯層和表現層。
正文
自從Servlet API發布以來,Java開發人員使用了不同的技術來開發Web應用程序。Web開發人員已經認識到了Model 2的優點。Model 2 指的是基於MVC模式的WEB開發框架。Model 2能把應用邏輯層和表現層較好地區分開來。Struts構建在Model 2 之上,它向Java開發人員提供了一個普通的控制器Servlet,還有集中式的資源配置、資源管理以及錯誤處理等功能。
這篇文章中我們引入了Model 2x。Model 2x可以將邏輯層和表現層更好地分離。我們先介紹Model 1和Model 2,然後講述一下Struts是如何實現MVC架構的,最後向讀者展示一下如何通過XML、XSLT來對現存的模式加以改進。
Model 1
理解Model 1是十分重要的,如果不熟悉Model 1將很難理解我們在文章中介紹的其他幾個架構。Model 1的基礎是JSP文件。JSP從Http請求中取得參數,調用所需的業務邏輯,處理Http對話,然後生成HTML頁面。
一個完整的Model 1項目包括一組JSP文件(這些文件大部分都是彼此獨立的)、一組所需的Java類和其他組件。一些早期的Web開發技術比如ASP、PHP都使用類似的模式來開發的。
Model 1主要也許是唯一的優勢就是簡單。Model 1中業務邏輯和顯示邏輯混合在JSP頁面中沒有進行抽象和分離,所以在進行快速和小規模的應用開發時,有非常大的優勢的。但用Model 1 開發復雜的項目的開發時,將不可避免地導致項目管理的困難。
Model 2, MVC, and Struts/JSP
圖1
圖1說明的是MVC架構中的三個部分,以及它們在Struts/JSP中的實現。
控制器(Controller):Struts的最主要的部件就是一個普通的控制器Servlet。控制器是每一個發送到Struts的Http請求的入口點。控制器把所有的請求經解釋後分發action。這些action是Struts Action類的子類。由開發人員負責實現它們。控制器也會自動的把Http請求中的參數加入到Form bean中。Action負責實現核心的業務邏輯,比如做EJB調用,通過Java Bean訪問模型(model)。在Struts中可以通過定義一個XML文件來描述請求URI、具體業務邏輯處理、代表客戶提供的數據的Form組件三者之間的映射關系。控制器的就是通過這個XML文件來定義的。
模型(Model):
Java beans就是模型的代表。這些bean分成3類:
窗體Bean(Form Bean)對象用來包裝HTML表單數據,當然也包括通過URL請求傳遞過來的數據。舉例來說,一個登陸頁面可能有兩個屬性(Property)login和password。Form Bean由Struts的ActionForm類擴展而來。
請求Bean(Request Bean)擁有用來生成HTML頁面的所需的信息。例如在一個表現銀行賬戶狀態的頁面中,請求Bean就應該有賬戶相關信息以及近期的交易記錄等等。
會話Bean(Session Bean)擁有同一用戶在不同Http請求間共享的對話信息。
視圖(View):
Struts控制器會把Http請求轉發到作為MVC視圖的JSP文件。這個JSP文件可以訪問窗體Bean、請求Bean、會話Bean,生成結果文檔(通常是一個HTML文檔),並發送到客戶端。Struts提供了四組JSP標志庫。
HTML:用來生成HTML標志,特別是用來自模型的數據填寫HTML表單。
Bean: 操作Bean。
Logic: 根據Bean值實現邏輯結構。
Template:處理頁面模板。
通過Struts標志的使用,你可以避免在視圖中使用任何的Java代碼。
Struts/JSP 缺點
雖然Struts/JSP較之其他模式有許多優勢,但它也存在著一些缺點和不足:
程序員可以把應用邏輯放入JSP。遇到問題時,開發人員可以很快地加以修改,經驗表明這實際上是陷阱。程序結構很容易變得復雜和難於管理。
JSP不能很好地支持XML,也就不能保證生成的結果文檔(XML或HTML文檔)將會是100%“格式良好”(Well-Formed)。
開發人員需要學習如何使用Struts的標志庫。事實上要理解這些Struts的標志庫,特別是Bean和HTML標志庫的確要花費比較長的時間。
你不能在視圖中用JSP來實現處理管道(processing pipeline)。只能做些簡單的include和forward,這樣很明顯就會限制了視圖的靈活性。例如,對布局計和風格的分離就會比較困難。
對JSP頁面的任何修改都會導致JSP的重新編譯,這樣是非常耗費時間的。
上述問題的解決方法必須要具備以下要求:
限制視圖對模式和一些定義明確的上下文環境信息的可見程度,比如項目資源的可見程度就應該受到控制。
強制使用格式良好的XML和HTML
能夠對在現存的語言或API起到槓桿作用
降低對視圖不同部分分離的難度,比如布局和風格的分離。
縮短開發周期
我們相信我們接下來討論的這個基於未加修改的Struts和XSLT的輕型框架可以滿足上述的要求。我們把這個新的架構叫做Model 2x
Model 2x架構概觀
Model 2x是 Struts和XSLT結合的產物。Model 2x基在視圖部分用XSLT和最後會被串行化成XML文件的Bean替代了原來Struts視圖部分的JSP文件,但原封不動地保留Struts的Controller和Model部分。
XSLT定義
作為一種W3C的正式標准,XSLT是用來對XML文檔進行轉化的一種語言。它是XSL(可擴展樣式表語言)的一部分。XPath是用作對XML文檔各部分進行定位的語言。在XSL樣式中,我們可以利用XPath表達式以一種緊湊而高效的形式選取XML文檔的一部分進行處理。
XSL/FO也是XSL的規范的一部分,用來描述顯示給讀者的頁面外觀。XSL/FO主要的一個應用就是生成PDF文檔。
XSLT和Struts的合成
把Struts和XSLT結合在一起的方法一是在JSP頁面中執行XSLT轉換。我們可以用標志庫來實現這一功能,比如你可以使用Jakarta項目中的一個XSL標志庫項目來實現。如果使用這種方式,那麼在JSP頁面中生成的是應該是XML而不是原來的HTML。借助XSLT樣式表,XML轉化成HTML或其他格式。然而這種方式需要對Struts本身加以修改。
生成HTML最通常的方式是由Struts的HTML標簽庫來生成,但這類標志庫與XML並不兼容,也就不能和XSLT結合起來使用。當然可以對HTML標志庫加以修改讓其輸出XHTML,這並不困難,但這就要修改現有的Struts 1.0 代碼。
此外,這一解決方案需要在四個不同的地方開發:Action 類(控制器)、模型Bean、JSP頁面、XSLT樣式表(視圖)。JSP與標簽庫的作用也只限於把模式Bean轉化成一個XML文檔。
第二種方法就是我們提出的Model 2x。這種方法會自動執行這一任務,而且把JSP頁面從我們的解決方案中刪除了。圖2向我們很好地展示了Model 2x設計的核心構成。
圖2
我們從圖2中可以看出來,Model 2x處理流程的最初部分和Struts的類似。請求被發送到Struts的控制器,然後又被分派給各自的業務邏輯處理單元(Action類的子類)。控制器創建ActionForm對象。請求的參數都保存到這個ActionForm對象中。Action類的子類生成結果Bean(Result Bean),然後把這些Bean交給視圖來顯示。
Model 2x和Struts處理流程不同的地方在於:Model 2x中用一個XSL Servlet配合XSLT樣式表實現了原來在Struts中由JSP實現的視圖部分。這個XSL Servlet首先根據Bean和上下文環境生成XML文檔,然後調用XSLT進行轉換。接下來我們會詳細地講述這個過程。由於我們可以把一個請求提交給任何一個已經在Struts配置文件中注冊的URL,所以在這個過程中不用對Struts做任何的修改。
XML文檔的生成
把一個對象轉換到一個Stream的過程我們稱之為串行化。在Java 1.1中引入了java.io.Serializable接口和相關的API。二進制串行化可以把一個Java對象轉化到二進制流,在網絡上傳輸或是保存到文件中。相比之下,XML串行化是把一個Java對象樹轉化到文本型的XML流中。
許多開放源代碼的軟件包,譬如Castor都可以用來執行XML串行化。在我們文章中提到的Model 2x案例中我們自行設計了一個簡單的XML串行化方案。這個方案中假設Bean的所有屬性是Java的基本類型或者java.util.Colleciton的子類。
這個方案會遞歸地對窗體Bean(Form Bean)、請求Bean(Request Bean)和Session bean進行自省,創建一個DOM樹。同時,也串行化了資源和Struts配置數據也就是上下文環境信息。圖3說明了這個過程
圖3 XML/XSL 工作流程
XSLT處理
在這個Model 2x案例中,XSLT轉換只限於樣式表對XML流的轉換。為了提高性能XSLServlet會對這個樣式表進行了緩存處理。XML流著由Struts的處理流程生成。你可以通過提供連續轉化或者使用更高級的配置來改進這個簡單的架構。Cocoon中你就可以看到這兩種方式的使用。Cocoon框架使用XML和XSLT構建服務器端的應用程序。Cocoon基於管道(Pipeline)的架構使其能夠更容易對內容和邏輯的加以分離、與大量不同的數據源交互也很方便。通過XSLT,Cocoon的輸出可以與不同的設備兼容,比如HTML、WAP等等。
圖3顯示的是XSLT的處理流程。下面一節提供了一個將內容和版面設計分離的一個例子。
轉化例子
TestForm是一個簡單的窗體bean,它只有兩個屬性:
public class TestForm extends ActionForm {
private String testString;
private List testList;
}
假設testString的值為My Test String,testList的值為One、two、Three,XML串行化代碼會生成一下XML片斷。在XML文檔中的元素名是可以預見的,這樣編寫樣式表的時候會簡單一些。
<page name="TestForm">
<request>
<TestForm>
<testString>My Test String</testString>
<testList>
<item>One</item>
<item>Two</item>
<item>Three</item>
</testList>
</TestForm>
</request>
</page>
簡單的XSLT模板把已經串行化的XML流轉換到XHTML片斷。
<xsl:template match="page">
<h2>Please enter some text and submit</h2>
<br/>
<form name="testForm" method="get" action="result">
<input type="text" name="testString"
value="{request/TestForm/testString}"/>
<br/>
<select name="outSelect">
<xsl:for-each select="request/TestForm/testList/item">
<option><xsl:value-of select="."/></option>
</xsl:for-each>
</select>
<br/>
<input type="submit" value="Submit"/>
</form>
<hr/>
</xsl:template>
經過轉化和HTML串行化,結果應該是如下
<h2>Please enter some text and submit</h2>
<br>
<form name="testForm" method="get" action="result">
<input type="text" name="testString" value="My Test String">
<br>
<select name="outSelect">
<option>One</option>
<option>Two</option>
<option>Three</option>
</select>
<br>
<input type="submit" value="Submit">
</form>
<hr>
Model 2x主要的優勢
這一節我們討論一下Model 2x較之其他模式的一些優勢。
業務邏輯和表現邏輯的分離
XML文件流根據模式(model)和上下文環境而生成,樣式表對XML文件流再進行加工。雖然一些XSLT轉換器可以支持一些擴展,通過這些擴展你可以在樣式表中調用Java或是其他類型的語言。但這些擴展往往缺乏移植性,而且使用起來比較麻煩。這樣就不能再把業務邏輯放入到XSL樣式表。
標准技術的使用
XPath是一種強大的表達式語言,用來從XML流中抽取出數據。我們可以通過使用<xsl:for-each>、<xsl:if>、<xsl:choose>這類的XSLT元素配合上模板屬性值,使用起來比Struts中的HTML、Logic、Bean這些標簽庫好很多。XSLT使用了W3C(World Wide Web Consortium)標准的語言,不但提供了和Struts標簽庫類似的功能,而且還有很大的提高。此外XSLT還有一些標簽庫沒有的強大功能,比如XSLT支持函數和遞歸。
其他需要考慮的問題
這章中我們討論在Model2x中的如何實現國際化、錯誤處理、當前的一些限制以及以後可能的對Model 2x的改進。
應用的國際化
本地化水平和目標語言的復雜程度會影響到了頁面的布局,還有文本消息和圖片。例如,阿拉伯文從右向左書寫而舊式的中文從上到下書寫。這些語言不但需要文本的翻譯,同時還需要完全不同的頁面布局。Struts開發人員通常把所有的文本信息和圖像資源的鏈接根據不同的地區保存到一起。根據用戶的地區,Struts就會調用適當的資源。
Model 2x通過自動把資源存儲到DOM中,然後把他們傳遞給視圖。為了提高資源訪問的效率可以對資源的讀取和DOM的建立緩存。Model 2x 只是簡單地把資源DOM插入到最終的DOM樹中。這個最終的DOM樹也包括了從Form Bean生成的動態內容。你可以輕松地用XPath在XSLT樣式表中訪問資源。應用的國際化不再通過Java資源捆綁技術實現,而是通過純XML技術——根據用戶當前的地區動態地切換樣式表來實現。
錯誤處理
驗證錯誤通常指的是在HTML表單中輸入的參數出現的錯誤。通常我們把這類錯誤和其他類型的錯誤區分開來。驗證錯誤以外的錯誤將被認為是較嚴重的錯誤。
在Struts中,可以在Form Bean中進行表單的驗證,出現錯誤時會返回ActionError對象。把這些對象存儲在請求中,然後串行化到Dom樹中。這樣樣式表就可以很容易地就把這些錯誤顯示到窗體中。同樣的,用戶可以也可以在Action中識別錯誤,然後把它們存儲到請求中,進一步地串行化,最後用樣式表來處理。
樣式表會依據錯誤的類型和錯誤的內容選擇是簡單的顯示一個錯誤的消息,還是將先前的頁面重新顯示,讓用戶修改數據後再提交。
工作流程
在這個Model 2x架構中,struts-config.xml文件並不能像在原來的Struts架構中那樣負責控制工作流程。不過,要說明的是這個問題實際上並不是Model 2x的問題,而是這篇文章我們這個實現方案的問題。你可以在你自己的實現方案中修正這個問題。
輸出
Model 2x架構的一個重要的特性就是能夠動態改變輸出的內容類型以及用戶接口的風格。比方說,同樣的應用項目可以針對老版本的浏覽器生成HTML 3.2代碼而同時又為新版本的浏覽器生成HTML 4.0的代碼。利用XSLT可以方便地獲得各種輸出格式: XHTML、XSL/FO、WML、簡單的文本、CSV、PDF、SVG等等。
XSLT處理流程
Model 2x標志著Web框架在分離版面設計與風格邏輯以及引導樣式表這兩方面有了提高。Apache的Cocoon框架就表現出s了這兩個優點。例如,某個樣式表可以定義在整個站點中某類特定表格的顯示樣式,比如下面這張樣式定義了用戶信息表格的顯示樣式。這張樣式表可能輸出如下一個表格:
<xsl:template match="customer-info">
<table>
<tr>
<td>Name</td>
<td><xsl:value-of select="name"/></td>
</tr>
</table>
</xsl:template>
而另一個樣式表也可以通過創建一個如下的嵌入表來設計這個表格。
<xsl:template match="table">
<table cellpadding="0" cellspacing="0" border="0" bgcolor="red">
<tr>
<td valign="top">
<table cellpadding="4" cellspacing="1" border="0">
<xsl:apply-templates select="tr"/>
</table>
</td>
</tr>
</table>
</xsl:template>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
性能
Model 2x當前的這個實現方案是動態的創建XML Dom樹,然後把它交給XSLT轉換器。通過使用SAX(Simple API for XML)顯著地提高了性能,特別表現在等待時間和大文檔的內存駐留上。此外,樣式表的編譯也提升了XSLT的處理次數。Xalan是Apache的一個XSLT處理器項目。它提供了一種把樣式表編譯成Java的class文件的機制,也就是是我們所說的translets。
客戶端處理
目前,IE5/6、Mozilla這樣的浏覽器已經能夠在客戶端執行XSLT轉換。如果要減輕Web服務器的負擔,可以在客戶端執行XSLT轉換。XInclude(XML Inclusions)可以從服務器下載資源和其他的數據,並建立緩存。XInclude提供了一種通用的方法來識別和處理它的內含物,同時還能夠提供很好的性能、更少的代碼冗余。當然這種方法也存在問題。最大的一個缺陷就是開發人員必須確保傳遞給客戶的XML、XSLT文檔必須是客戶有權看到的內容。
通過Model 2x來提升Struts的性能
總之,Model 2x中使用XML和XSLT來鼓勵開發人員將業務邏輯和表現邏輯加以分離,這樣web應用程序更接近MVC最基本的承諾。此外它還具有一些其它的優勢比如輸出的文檔符合XML的文檔格式、標准化的語言的使用、更好的表現層適應性,更短的開發周期。