利用Tiles簡(jiǎn)化和增強(qiáng)Struts的 JSP開(kāi)發(fā)
JSP(JavaServer Pages)技術(shù)通過(guò)includes支持應(yīng)用程序?qū)ο蟮闹赜茫篿ncludes允許其他文件(包括其他JSP文件)在編譯時(shí)或者在應(yīng)用程序運(yùn)行時(shí)動(dòng)態(tài)地被嵌入JSP文件。該特性非常有用,它可以將頁(yè)面的共用組件,諸如報(bào)頭、頁(yè)腳以及菜單提取出來(lái),作為可重用組件被多個(gè)文件所用。
Includes功能很強(qiáng)大,并且可以節(jié)約時(shí)間,但是他們卻會(huì)帶來(lái)組件大量重復(fù)的潛在問(wèn)題。 每個(gè)嵌入共用組件的JSP文件都復(fù)制include定義。如果被包含的文件的名稱改變了,那么每個(gè)使用Include引用這些文件的文件都需要被更新。
Tiles框架就是為了解決該局限性和增強(qiáng)Struts框架的功能而創(chuàng)建的。Tiles利用includes通過(guò)允許用戶定義模板(布局),然后指定模板內(nèi)容的裝入方式,而擴(kuò)展了重用的概念。
借助JSP的include范例,每個(gè)JSP通過(guò)include指定其模板和明確地給出內(nèi)容的裝入方式。大多數(shù)JSP的模板是相同的:在相同的地方裝入相同的文件,只有加入獨(dú)特內(nèi)容(主體內(nèi)容)部分是不同的。借助Tiles,可以使用一個(gè)外部配置文件定義一個(gè)主模板JSP,后者指定每個(gè)用于裝填模板的include,然后定義哪個(gè)內(nèi)容需要加入該模板。比如,設(shè)想一下你有一個(gè)典型的網(wǎng)站模板:頁(yè)面頂端有一個(gè)報(bào)頭(標(biāo)題),左側(cè)有一個(gè)菜單,中間部分為主體內(nèi)容,底部是頁(yè)腳。如果你僅僅使用JSP的includes來(lái)實(shí)現(xiàn)這種模板,每個(gè)具有這種模板的JSP不得不明確包含報(bào)頭、菜單、頁(yè)腳各部分以及頁(yè)面的主體內(nèi)容。該頁(yè)面中唯一獨(dú)特的部分就是主體內(nèi)容。然而,如果你使用Tiles來(lái)實(shí)現(xiàn)該模板,那么,你只需要?jiǎng)?chuàng)建一個(gè)包含報(bào)頭、菜單和頁(yè)腳的JSP文件,基于能夠傳遞給模板的用來(lái)指定哪個(gè)JSP文件包含主體內(nèi)容的屬性,就可以動(dòng)態(tài)地包含主體內(nèi)容。根據(jù)你的愿望,該Tiles模板能夠被一次再一次地重復(fù)用于許多頁(yè)面,你的內(nèi)容JSPs必須包含的東西就是位于頁(yè)面中部的獨(dú)特的主體內(nèi)容。
將Tiles添加到你的應(yīng)用程序中
現(xiàn)在你已經(jīng)知道了使用Tiles框架的好處,下面是將Tiles添加到你的Struts應(yīng)用程序中所必要步驟:
- 將Tiles標(biāo)記庫(kù)描述符(TLD,Tag Library Descriptor)文件添加到該應(yīng)用程序。
- 創(chuàng)建模板JSPs。
- 更新現(xiàn)有的JSP以使用模板。
- 創(chuàng)建文件tiles-defs.xml。
- 更新struts-config.xml 文件中的傳遞定義(forward definitions),并且將Tiles插件添加到該文件中。
- 重新打包,并且運(yùn)行更新后的該應(yīng)用程序。
下面詳細(xì)講述該過(guò)程的每個(gè)步驟。
將Tiles 標(biāo)記庫(kù)描述符文件添加到應(yīng)用程序。更新你的Struts應(yīng)用程序,以便使用Tiles的第一步是將Tiles標(biāo)記庫(kù)描述符文件添加到該應(yīng)用程序中。該步驟是必須的,只有這樣JSP才能使用Tiles標(biāo)記庫(kù)。Tiles標(biāo)記庫(kù)允許你在JSPs中使用Tiles模板。要將Tiles標(biāo)記庫(kù)描述符文件添加到該應(yīng)用程序中,需要從Struts發(fā)布庫(kù)目錄(例如,c:\java\jakarta-struts-1.1\lib)中將 struts-tiles.tld 文件拷貝到你的應(yīng)用程序的/WEB-INF/tlds目錄中。下一步,在你的應(yīng)用程序的Web部署描述符文件(web.xml)中添加一個(gè)標(biāo)記庫(kù)描述符(TLD)項(xiàng),并按如下所示注冊(cè)Tiles的標(biāo)記庫(kù)描述符:
<taglib>
<taglib-uri>
/WEB-INF/tlds/struts-tiles.tld
</taglib-uri>
<taglib-location>
/WEB-INF/tlds/struts-tiles.tld
</taglib-location>
</taglib>
現(xiàn)在可以通過(guò)使用如下所示的代碼,從你的JSPs中引用TLD:
<%@ taglib
uri="/WEB-INF/tlds/struts-tiles.tld"
prefix="tiles" %>
創(chuàng)建模板JSPs。既然你已經(jīng)注冊(cè)了Tiles的標(biāo)記庫(kù)描述符,那么現(xiàn)在就可以創(chuàng)建模板JSPs。代碼清單 1所示的模板描述了Tile的基本概念。該模板是在你應(yīng)用程序已有的頁(yè)面結(jié)構(gòu)的基礎(chǔ)上創(chuàng)建的。
請(qǐng)注意,代碼清單 1中模板頁(yè)面的主體內(nèi)容采用黑體字。對(duì)于每個(gè)不同的頁(yè)面,該部分內(nèi)容是不相同的。然而,頁(yè)面的其他部分對(duì)這幾個(gè)頁(yè)面來(lái)說(shuō)是相同的,這樣就可以將它們提取出來(lái)構(gòu)成一個(gè)通用的模板。
3個(gè)JSP文件--mainLayout.jsp、 header.jsp和 footer.jsp--構(gòu)成了該模板。mainLayout.jsp 文件如下所示:
<%@ taglib
uri="/WEB-INF/tlds/struts-tiles.tld"
prefix="tiles" %>
<html>
<head>
<title>
<tiles:getAsString name="title"/>
</title>
</head>
<body>
<tiles:insert attribute="header"/>
<tiles:insert attribute="body"/>
<tiles:insert attribute="footer"/>
</body>
</html>
該JSP文件定義了布局的模板,用于嵌入其他模板JSP以及主體內(nèi)容。主體內(nèi)容和其他模板JSP通過(guò)<tiles:insert>標(biāo)記來(lái)嵌入。這些標(biāo)記指定在Tiles配置文件中定義的屬性的名稱,這些屬性的值是在運(yùn)行時(shí)應(yīng)被插入到JSP中的JSPs的名字。請(qǐng)注意<tiles:getAsString>標(biāo)記的用法。該標(biāo)記的作用與<tiles:insert> 標(biāo)記類似,但是與后者使用被包含的頁(yè)面的名稱作為指定的屬性值不同,它使用文本串來(lái)作為指定的屬性值。該方法適用于將小塊內(nèi)容動(dòng)態(tài)地插入模板,但不保證自有包含文件也被插入。
下面是報(bào)頭和頁(yè)腳模板JSPs:
header.jsp
<font size="+1">
ABC, Inc. Human Resources Portal
</font><br>
<hr width="100%" noshade="true">
footer.jsp
<hr width="100%" noshade="true">
Copyright © ABC, Inc.
這種報(bào)頭和頁(yè)腳JSPs都非常的簡(jiǎn)單,不包含太多的HTML。這些JSPs的內(nèi)容可以直接放入mainLayout.jsp文件中,內(nèi)容頁(yè)面仍然只需包含頁(yè)面的主體內(nèi)容。 然而,將頁(yè)面拆分成多個(gè)小塊可提高模板的使用靈活性。如果你需要一些頁(yè)面必須使用定制的報(bào)頭和頁(yè)腳而其他頁(yè)面使用標(biāo)準(zhǔn)報(bào)頭和頁(yè)腳,則可以將報(bào)頭和頁(yè)腳分離成離散的組件,以便使你能做到這一點(diǎn)。你只需在模板級(jí)別上定義報(bào)頭和頁(yè)腳屬性的值,對(duì)于需要定制的報(bào)頭或頁(yè)腳(或二者),可在頁(yè)面這一級(jí)上用新的值取代相應(yīng)的值。
更新現(xiàn)有的JSPs以使用模板。在創(chuàng)建了模板JSPs之后,更新應(yīng)用程序的原始JSPs,以便只包含其頁(yè)面的主體內(nèi)容。要達(dá)成此目的,從原始頁(yè)面中移除共用的模板部分。代碼清單 2顯示了更新后的只包含主體內(nèi)容的示例頁(yè)面。
代碼清單 2中的已更新的頁(yè)面不再包含內(nèi)容的報(bào)頭和頁(yè)腳部分。在運(yùn)行時(shí),模板JSPs用共用布局內(nèi)容填充已更新的頁(yè)面來(lái)創(chuàng)建完整的頁(yè)面。
創(chuàng)建文件tiles-defs.xml。有兩種聲明Tiles模板的方法:
- 通過(guò)在每個(gè)模板JSP中所包含的主JSP中的Tiles標(biāo)記進(jìn)行聲明;
- 通過(guò)在XML配置文件中對(duì)Tiles模板進(jìn)行聲明。
下面所述的例子使用配置文件方式,因?yàn)檫@種方式更加靈活,并且更加容易維護(hù)。代碼清單 3顯示了聲明模板的tiles-defs.xml文件。該文件應(yīng)該位于你的應(yīng)用程序的/WEB-INF/ 文件夾中。
在該tiles-defs.xml 文件中有兩個(gè)Tiles的定義。該文件中的第一個(gè)定義聲明了名為main.layout的模板。通常,模板定義指定用于頁(yè)面的模板,也會(huì)指定屬性列表--這些屬性的值將會(huì)被用來(lái)完成模板的填充。 頁(yè)定義擴(kuò)展模板定義,并且為在擴(kuò)展模板中定義的屬性提供相應(yīng)的值。
請(qǐng)注意,代碼清單 3中的第一個(gè)定義--main.layout--用put標(biāo)記定義了4個(gè)屬性。這些屬性對(duì)于path屬性指定的模板JSP--mainLayout.jsp--可用。該模板JSP使用這些屬性來(lái)提供其內(nèi)容的位置。另外,與title屬性一樣,這些屬性能夠被用以支持文本串。mainLayout.jsp中使用了title屬性,因此它可以根據(jù)由頁(yè)面定義設(shè)置的值包含一個(gè)動(dòng)態(tài)標(biāo)題(title),從而擴(kuò)展了模板的定義。
代碼清單 3的tiles-defs.xml 文件中的第二個(gè)定義聲明了一個(gè)被稱為search.page的頁(yè)面定義。該定義擴(kuò)展了main.layout定義,并且為在模板定義中無(wú)值的屬性提供值。該定義可以重設(shè)模板定義中的任何屬性,但在本例中只有標(biāo)題(title)和主體(body)屬性被重設(shè)。
更新struts-config.xml 文件中的傳遞定義,并且將Tiles插件添加到該文件中。在創(chuàng)建了Tiles配置文件之后,更新你的應(yīng)用程序的struts-config.xml 文件以指向Tiles定義,而不是直接指向每個(gè)已經(jīng)轉(zhuǎn)換成使用Tiles的頁(yè)面的JSPs,并把Tiles 插件添加到struts-config.xml文件中。
如果沒(méi)有Tiles,正向作用定義就直接指向JSPs。有了Tiles,他們就指向Tiles配置文件中的頁(yè)面定義。比如說(shuō),在這之前應(yīng)用程序的搜索行動(dòng)(search action)直接指向search.jsp,如下所示:
<action
path="/search"
type="com.jamesholmes.minihr
.SearchAction"
name="searchForm"
scope="request"
validate="true"
input="/search.jsp"/>
然而,有了Tiles之后,該動(dòng)作就指向搜索頁(yè)面的Tiles定義,如下所示:
<action
path="/search"
type="com.jamesholmes.minihr
.SearchAction"
name="searchForm"
scope="request"
validate="true"
input="search.page"/>
對(duì)于所有Struts配置文件中決定使用Tiles的傳遞和行動(dòng)(forward and action)定義,如下例所示,用合適的Tiles頁(yè)面定義引用來(lái)代替相應(yīng)的JSP引用。
為了將Tiles插件添加到應(yīng)用程序,將以下代碼段添加到你的struts-config.xml文件中:
<!-- Tiles Configuration -->
<plug-in className=
"org.apache.struts.tiles.TilesPlugin">
<set-property property=
"definitions-config"
value="/WEB-INF/tiles-defs.xml"/>
</plug-in>
該段代碼使得應(yīng)用程序在啟動(dòng)時(shí)載入Tiles插件。請(qǐng)注意,Tiles配置文件由set-property標(biāo)記來(lái)指定。通過(guò)提供一個(gè)以逗號(hào)分隔的文件列表,可以用該標(biāo)記來(lái)指定多個(gè)配置文件。
代碼清單 4顯示了整個(gè)Struts配置文件。粗體顯示部分是被修改或者被添加的部分。重新打包,并且運(yùn)行更新后的該應(yīng)用程序。因?yàn)樵谠撨^(guò)程中,并沒(méi)有Java代碼被修改,所以不必要重新編譯應(yīng)用程序。然而,因?yàn)樘砑恿艘恍┪募蛯?duì)一些做了修改,所以在應(yīng)用程序運(yùn)行之前,需要重新打包和重新部署。一旦使你的更新之后的應(yīng)用程序能夠運(yùn)行,那么所有的功能都會(huì)和以前一樣發(fā)揮其作用,但是現(xiàn)在你可以毫不費(fèi)力地添加新的頁(yè)面和對(duì)應(yīng)用程序做出全局性的改變。
結(jié)論
通過(guò)提供了一個(gè)功能強(qiáng)大的促進(jìn)頁(yè)面組件重用的模板創(chuàng)建系統(tǒng),Tiles框架極大地提高了核心Struts框架的價(jià)值。通過(guò)將Tiles框架用于你的JSP應(yīng)用程序,可以節(jié)約大量的時(shí)間,并且大大提高你的Struts應(yīng)用程序的開(kāi)發(fā)效率。
James Holmes (james@)是一名獨(dú)立的Struts項(xiàng)目方面的Java咨詢專家和撰稿人。他是《Struts:完整的參考手冊(cè)》的作者,還是《Java的藝術(shù)》一書的合著者之一(兩本書都由McGraw-Hill/Osborne出版)