入門 html
本教程講述如何使用 Tiles 框架來建立可重用的表示組件。(在最初建立它時,Tiles 框架被命名爲 Components。 後來改變了名稱是由於「components(組件)」表明了太多不一樣的東西,不過原先的名稱的精髓仍然獲得了保留。) 然而,除了站點佈局以外,使用 tile 還能作其餘許多事情。例如,您能夠劃分表示層以更好地重用佈局、HTML以及其餘可視組件。 web
本教程力圖揭示使用 Tiles 框架的基礎,而後讓您的知識再上一個臺階。當完成本教程的學習時,您將可以使用更高級的 Tiles 特性來建立可重用組件。 數據庫
注意:貫穿本教程,咱們交替使用術語 tile 和 頁面,由於任何 Web 資源均可以是 tile。 一個 tile 佈局表明一種特殊類型的 tile,便可以用來在它內部放置其餘 tile。 一個 tile 佈局能夠用做另外一個 tile 佈局內的 tile。 apache
明確地說,本教程: 編程
若是您發現本身在每一個頁面上都要編寫三行相同的 JSP 代碼,或者您想容易地定義複雜的模版佈局,那麼您就會從本教程中獲益。 瀏覽器
本教程假設您徹底理解 Java 編程、MVC(Model-View-Controller,模型-視圖-控制器)、Model 2 和 JSP 技術。雖然良好的 Struts 背景會讓您從本教程中得到最大好處,不過只要您精通 JSP 編程,就應該可以理解本教程講述的大多數內容。 tomcat
爲完成本教程的學習,您將須要: 架構
請參閱參考資料以瞭解關於這些材料和附加參考資料的信息。
Tiles 框架和體系結構
Tiles 框架完全揭示了 jsp:includes 內部的概念 ―― 從而容許您更靈活地建立可重用的頁面。使用 Tiles 框架,開發人員可以經過組合可重用的 tile 來構建頁面。您應該將 tile 看做是可視組件。
Tile 佈局是容許在其上放置其餘 tile 的特殊 JSP 頁面。 Tile 佈局控制了 tile 在頁面上的放置位置。從許多方面看來,tile 佈局都和模板佈局相似。事實上,若是之前使用過 Struts,那麼您會注意到 Tile 框架與模板自定義標籤庫向是後兼容的。
本教程中出現的術語初看起來可能有點難以招架,所以在更詳細地討論 Tiles 框架以前,讓咱們首先定義一些重要術語。
術語詞彙表
Tiles Struts 用來建立表示組件的模板框架。 頁面 tile 佈局包括的 Web 資源。 Tile 同頁面。 區域 tile 佈局中插入其餘 tile 的範圍。 區域擁有諸如頁眉、頁腳之類的邏輯名稱。 Tile 佈局 描述其餘頁面應該定位在何處的 JSP 頁面。Tile 佈局充當模板,定義了插入其餘 tile 的區域。 一個 tile 佈局能夠是另外一個 tile 佈局的 tile。 定義 定義用於調用某個 tile 佈局的參數。從某些方面看來,tile 佈局工做起來就像一個顯示函數。要使用某個 tile 佈局,可以使用 tiles:insert 標籤來調用它。調用 tile 佈局時要向它傳遞參數。這些參數將成爲該 tile 佈局的屬性;例如,參數將放入 tile 範圍。
調用 tile 時傳遞的參數能夠是其餘 JSP 頁面或 Web 資源,您能夠將它們插入佈局中的預約義位置(稱爲 區域)。參數還包含可以插入 tile 佈局的字符串。事實上,能夠將許多類型的對象做爲參數傳遞給 tile。這些參數會成爲僅對該 tile 可用的 tile 範圍內的屬性。
tile 範圍 相似頁面範圍,由於 tile 範圍比請求範圍更特殊化。 tile 範圍容許 tile 用戶給 tile 傳遞參數(稱爲屬性)。tile 範圍容許您傳遞僅對該 tile 佈局或 tile 可用的變量(稱爲屬性)。 特殊自定義標籤容許您將屬性從 tile 範圍複製到頁面、請求、會話或應用程序範圍,或者將屬性做爲包含的 Web 資源來顯示。
有些編程語言,好比 C++、Visual Basic 和 Python,容許您向函數和方法傳遞默認參數。爲進一步擴展這個顯示函數,Tiles 框架還容許您向 tile 佈局傳遞默認參數。爲此,您必須定義一個 tile 定義 。 Tile 定義容許您定義 tile 的默認參數。Tile 定義(definition)能夠在 JSP 代碼或 XML 中定義。
像類擴展其餘類同樣,定義能夠擴展其餘定義。經過使用定義和 tile 佈局,您可以建立可重用的顯示組件。
能夠結合 Struts 使用 Tiles,也能夠在沒有 Struts 的狀況下使用 Tiles。要結合 Struts 使用 Tiles,您將使用 Struts 附帶的 Tiles 標籤庫。 此外,Tiles 框架包括它本身的 RequestProcessor,用於將 tile 佈局做爲 ActionForward 來處理――從而容許您轉到 tile 定義而不是轉到 JSP 頁面。Tile 是經過在它的 RequestProcessor 中重寫processActionForward 來實現這點的。
典型的 tile 佈局可能爲頁眉、頁腳、菜單和正文定義矩形區域,如圖 1 所示。
圖 1 所示的區域能夠映射到相似圖 2 所示的某個 Web 站點。
注意,只需傳遞正確的參數,就可以容易地從新定義這個應用程序的可重用部分。 例如,僱員清單可能使用相同的頁眉和頁腳,可是使用不一樣的菜單和正文,同時仍然可以使用 tile 佈局所定義的所有通用佈局區域。 這樣容許對不一樣的內容重用相同的 tile 佈局。 與包括 HTML 標記不一樣的是,您將在標記中包括內容。
1. Tile 佈局
若是站點可以重用相同的佈局(使用 HTML 表格來實現)和圖像,而沒必要重複相同的 HTML 代碼,這樣不是很好嗎?
Tile 在爲站點建立共同的外觀方面特別出色。話雖這樣說,許多開發人員並無認識到 Tiles 在建立用 JSP 實現的可重用組件方面一樣也很出色。
若是您發現本身在多個頁面上重複相同的 HTML 代碼,就可考慮對那些頁面使用 tile 佈局。相似地,若是在不一樣頁面上的不一樣地方使用相同的 HTML 或 JSP 標籤,這種情形也很適合使用 tile 來建立小型可視組件。
做爲 Tiles 框架的一個例子,下面將重構一個簡單的股票報價應用程序來利用 tile 佈局,如圖 3 所示。
這個簡單的示例應用程序主要包含一個股票報價頁面,它具備一個接受單個參數(即股票代碼)的表單(index.jsp)。 另外一個頁面顯示股票報價的值(quote.jsp)。
研究一下下面這兩個代碼清單。您將重構它們以使用各類各樣的 tile 佈局。
index.jsp
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %> <%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %> <html> <head> <title>Stock Quote</title> </head> <body> <table width="500" border="0" cellspacing="0" cellpadding="0"> <tr> <td> </td> </tr> <tr bgcolor="#36566E"> <td height="68" width="48%"> <div align="left"> <img src="images/hp_logo_rickhightower.gif" width="220" height="74"> </div> </td> </tr> <tr> <td> </td> </tr> </table> <html:form action="Lookup"> <table width="45%" border="0"> <tr> <td><bean:message key="app.symbol" />:</td> <td><html:text property="symbol" /></td> </tr> <tr> <td colspan="2" align="center"><html:submit /></td> </tr> </table> </html:form> </body> </html>
quote.jsp
<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %> <html> <head> <title>Stock Quote</title> </head> <body> <table width="500" border="0" cellspacing="0" cellpadding="0"> <tr> <td> </td> </tr> <tr bgcolor="#36566E"> <td height="68" width="48%"> <div align="left"> <img src="images/hp_logo_rickhightower.gif" width="220" height="74"> </div> </td> </tr> <tr> <td> </td> </tr> <tr> <td> </td> </tr> <tr> <td> </td> </tr> <tr> <td> <bean:message key="app.price" />: <%= request.getAttribute("PRICE") %> </td> </tr> <tr> <td> </td> </tr> </table> </body> </html>
欲學習如何使用 Tiles 框架,您首先必須編寫一個 tile 佈局,而後重構上述兩個例子頁面,以便它們沒必要重複如此多的 HTML 代碼。
爲了建立一個 tile 佈局,您須要作如下事情:
因爲找出兩個頁面之間的類似之處須要 HTML 佈局和 Web 站點適用性方面的技能,事實證實這項工做更像一門藝術,而不是像一門科學。因爲某些緣由,擁有紫色的頭髮和紋身是有所幫助的。若是您不這樣認爲,能夠問個人朋友 Boo。
本教程重點集中於 Struts,而不是 HTML 佈局和 Web 站點適用性方面的必要技能。 所以,您不會了解關於紋身和紫色頭髮方面的內容。事實上,例子中的 HTML 佈局是刻意簡單化的,以防分散咱們對 Tiles 框架的注意力。
一旦找出了頁面之間的類似之處(這是困難的部分),您就可以建立新的佈局頁面(這是容易的部分)。爲了建立一個 tile 佈局,您必須作如下事情:
將 tile 標籤庫導入 JSP,同時導入須要的其餘任何標籤庫,以下所示(siteLayout.jsp):
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %> <%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %> <%@ taglib uri="/WEB-INF/struts-tiles.tld" prefix="tiles" %
注意:要使用 tile 標籤庫,不要忘了包括 web.xml文件中的標籤庫:
<taglib> <taglib-uri>/WEB-INF/struts-tiles.tld</taglib-uri> <taglib-location>/WEB-INF/struts-tiles.tld </taglib-location> </taglib>
接下來使用字符串參數顯示諸如頁面標題之類的內容。您不只須要更改頁面的內容,並且還須要更改出如今瀏覽器中的標題。爲此,須要傳入 tile 佈局將要使用的標題:
<html> <head> <title> <tiles:getAsString name="title" ignore="true"/> </title> </head>
注意該代碼中使用了tiles:getAsString 標籤來顯示字符串參數。您不只可以傳遞字符串參數,並且可以傳遞要插入這個頁面的其餘頁面。這裏假設調用 JSP 頁面向這個 tile 佈局傳遞了一個標題;不然,標題將是空白。
注意: ignore 屬性:
ignore 屬性若是爲 true,這意味着在缺失該屬性的狀況下忽略它。不然,若是 ignore 屬性爲 false,那麼在沒有傳遞該參數的狀況下,Tiles 框架將拋出異常,頁面將不會顯示出來(false 是默認值)。
要插入內容 JSP,可以使用 tiles:insert標籤,它插入該框架做爲 tile 來引用的任何頁面或 Web 資源。這個標籤實際上在 tile 佈局中定義了一個區域。 記住,tile 佈局的目標是將 tile 佈置到該佈局中。下面是向該佈局插入一個 tile 的例子:
<tiles:insert attribute="content"/>
上面這個例子很是簡單。若是想要插入一個 tile,並向它傳遞當前頁面範圍內的項,那該怎麼辦呢?例如,使用 Tiles 框架給 header.jsp 傳遞一個標題參數(在 tile 範圍內)是能夠作到的。
在插入 tile 的任什麼時候候,您均可以選擇性地給它傳遞參數。傳遞給 tile 的參數將被放入該 tile 的標題範圍(稱爲「標題屬性」)。例如,除了讓標題顯示在瀏覽器的標題欄以外,可能還但願該標題出如今頁面的頁眉區域。
header.jsp文件將完成這個任務。雖然標題變量在該 tile 佈局頁面範圍以內,但它不在該 tile 佈局所插入的 tile 的範圍以內。脆弱方法每一個 tile 和 tile 佈局都獲取它本身的環境 ―― 也就是它本身的 tile 範圍。於是,您必須像下面這樣給頁眉 tile 傳遞該 tile 變量:
<tiles:insert attribute="header" ignore="true"> <tiles:put name="title" beanName="title" beanScope="tile"/> </tiles:insert>
tiles:put 標籤將這個 tile 佈局範圍內的 tile 參數放進頁眉 tile 的範圍。而後頁眉 tile 就可以像 tile 佈局所作的那樣,經過 tiles:getAsString 標籤來使用這個參數。參數名稱就是頁眉的 tile 範圍內的屬性名稱。 bean 參數是當前範圍內(siteLayout.jsp)的 bean 的名稱。 beanScope 是您在其中查找這個屬性的範圍(可能的值是頁面、tile、請求、會話和應用程序)。 您能夠從任何範圍向該 tile 傳遞 bean。
接下來,您會看到 quote.jsp 和 index.jsp 將要使用的這個新佈局頁面(siteLayout.jsp)的完整清單:
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %> <%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %> <%@ taglib uri="/WEB-INF/struts-tiles.tld" prefix="tiles" %> <html> <head> <title> <tiles:getAsString name="title" ignore="true"/> </title> </head> <body> <table width="500" border="0" cellspacing="0" cellpadding="0"> <tr bgcolor="#36566E"> <td height="68" width="48%"> <div align="left"> <img src="images/hp_logo_rickhightower.gif" width="220" height="74"> </div> </td> </tr> <tr> <td height="68" width="2000"> <tiles:insert attribute="header" ignore="true"> <tiles:put name="title" beanName="title" beanScope="tile"/> </tiles:insert> </td> </tr> <tr> <td> <div align="center"> <tiles:insert attribute="content"/> </div> </td> </tr> <tr> <td> <tiles:insert attribute="footer" ignore="true"/> </td> </tr> </table> </body> </html>
請花點時間研究一下上面的代碼。注意 tile 是如何插入不一樣區域的(頁眉、頁腳、內容),以及如何利用 HTML 佈局來爲 tile 定義區域,從而爲應用程序定義完整的佈局。
如今已經定義好了使用 tiles 的 tile 佈局,您須要使用該佈局。 index.jsp 和 quote.jsp 都將使用同一個佈局。雖然這對兩個頁面來講彷佛是大量的工做,可是對於真實的 Web 應用程序,你可能會對 20 個或更多的頁面使用同一個佈局。 經過這種方式,您沒必要在 20 個位置重複 HTML 或包括 JSP 片段。
注意:爲何不就使用 jsp:include 呢?
在適當的位置包括 JSP 片段是重用 HTML 的脆弱方法。設想一下包括相同的 5 個 JSP 片段的 20 個頁面 ―― 您必須重複 100 次。
爲了使用 tile,您須要執行如下步驟:
經過使用 tile 佈局,您可以在一個位置中將站點佈局所須要的整個 HTML 外部化,而後只需將它插入每一個頁面。觀察一下下面的例子,它顯示瞭如何把 tile 佈局插入 index.jsp:
<%@ taglib uri="/WEB-INF/struts-tiles.tld" prefix="tiles" %> <tiles:insert page="/siteLayout.jsp" flush="true"> <tiles:put name="title" type="string" value="Get Rick Hightower Stock Quote" /> <tiles:put name="header" value="/header.jsp" /> <tiles:put name="footer" value="/footer.jsp" /> <tiles:put name="content" value="/indexContent.jsp"/> </tiles:insert>
如今,當您想要在 quote.jsp 中作相同的事情時,只需更改內容和頁眉。
您須要使用插入標籤來調用 tile 佈局(顯示函數)。(注意用來將 tile 佈局插入當前頁面的 tiles:insert標籤):
<tiles:insert page="/siteLayout.jsp" flush="true">
page 屬性指定了上一節中定義的 tile 佈局。若是 flush 屬性被設置爲 true,這個 tile(以及到目前爲止的頁面)將在頁面的其他部分以前(或在緩衝區滿而迫使執行刷新時)寫到瀏覽器。
要更改 quote.jsp 和 header.jsp 之間的頁面 tile,可以使用子標籤 tiles:put:
<tiles:put name="title" type="string" value="Get Stock Quote" />
注意 tiles:put 是如何向 tile 佈局傳遞字符串參數的。 tiles:put 的 name 屬性標籤指定了參數名稱。tiles:put 的type 屬性指定了參數的類型。最後,value 參數用於傳遞 title 屬性的值。這容許您在使用 tiles:insert 標籤來調用 tile 佈局(顯示函數)時,把簡單的字符串做爲參數來傳遞。這些參數將成爲該 tile 佈局屬性;也就是被插入該 tile 佈局的 tile 範圍。
注意您是如何將三個 tile 做爲頁眉、頁腳和內容參數來傳遞的( header.jsp、footer.jsp 和indexContent.jsp):
<tiles:put name="header" value="/header.jsp" /> <tiles:put name="footer" value="/footer.jsp" /> <tiles:put name="content" value="/indexContent.jsp"/>
header.jsp 頁面將被插入該 tile 佈局的頁眉區域。 footer.jsp 頁面將被插入該 tile 佈局的頁腳區域。indexContent.jsp 頁面將被插入該 tile 佈局的內容區域。 若是想插入不一樣的內容和 tile,只需改變內容參數的值。
注意用於 index.jsp 的表單再也不駐留在 index.jsp 中。該表單如今駐留在 indexContent.jsp中,以下面所列出的:
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %> <%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %> <html:form action="Lookup"> <table width="45%" border="0"> <tr> <td><bean:message key="app.symbol" />:</td> <td><html:text property="symbol" /></td> </tr> <tr> <td colspan="2" align="center"><html:submit /></td> </tr> </table> </html:form>
除了將 tile 指定爲 JSP 頁面外,您還可以在 tiles:put 標籤的正文內將文本做爲 tile 來傳遞。quote.jsp所作的正好就是這樣:
<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %> <%@ taglib uri="/WEB-INF/struts-tiles.tld" prefix="tiles" %> <tiles:insert page="/siteLayout.jsp" flush="true"> <tiles:put name="title" type="string" value="Rick Hightower Stock Quote" /> <tiles:put name="header" value="/header.jsp" /> <tiles:put name="footer" value="/footer.jsp" /> <tiles:put name="content" type="string"> <bean:message key="app.price"/> <%= request.getAttribute("PRICE") %> </tiles:put> </tiles:insert
注意 tiles:put 標籤的標籤體包含 quote.jsp 的內容。其餘每項內容都是該佈局所定義的,都與上一個例子中使用的 tile 相同。這樣的優勢是可以減小系統中的 JSP 頁面的數目。 關於哪一種方法工做得最好,好久以來一直存在爭議,個人結論是它取決於 put 標籤體中有多少代碼。
您看到這裏存在的問題了嗎?存在這樣一條規則:不要重複您本身(Don't repeat yourself,DRY),而您已經違背了這點規則。您知道爲何嗎?
2. Tile 定義
遺憾的是,quote.jsp 和 index.jsp 都違背了 DRY 規則,它們都重複定義了頁眉和頁腳參數。因爲它們都使用相同的參數值,所以沒必要在兩個頁面上重複相同的參數是很理想的。
設想有這樣一個真實的應用程序,其中的 tile 佈局包括更多的區域(好比說 8 個)和使用該 tile 佈局的更多頁面。您會發現每次想使用某個 tile 佈局時都要重複每一個參數是一件很痛苦的事情。既然大多數頁面都將使用相同的頁眉和頁腳,那麼在單個位置而不是在每一個頁面定義它們將會帶來好處。
回顧一下前面的顯示函數類比,tile 佈局在某些方面相似一個顯示函數。 您使用 tiles:insert 來調用 tile 佈局,而且使用 tiles:put 來傳入參數。參數是可以插入 tile 佈局區域的其餘 JSP 頁面或字符串。
您如今須要定義對應於頁眉和頁腳區域的默認參數的能力。Tile 框架還容許您使用定義(definition)來給 tile 佈局傳遞默認參數。
在本節中,您將學習如何建立和使用定義。定義(definition)定義了 tile 佈局的默認參數。定義(definition)能夠在 JSP 代碼或 XML 中定義。在結束本節的學習時,您將可以同時使用這兩種方法建立定義。
您將發現使用 JSP 頁面來建立定義是最容易的方法,由於它須要最少的配置。
要建立一個 JSP 定義,請執行如下步驟:
在下面的清單中,siteLayoutDefinition.jsp 定義了這樣一個定義,它使用 siteLayout.jsp 做爲 tile 佈局,並定義了頁眉和頁腳的默認參數(以及其餘參數):
<%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %> <%@ taglib uri="/WEB-INF/struts-tiles.tld" prefix="tiles" %> <logic:notPresent name="siteLayoutDef" scope="application"> <tiles:definition id="siteLayoutDef" page="/siteLayout.jsp" scope="application"> <tiles:put name="title" type="string" value="Rick Hightower Stock Quote System" /> <tiles:put name="header" value="/header.jsp" /> <tiles:put name="footer" value="/footer.jsp" /> <tiles:put name="content" type="string"> Content goes here </tiles:put> </tiles:definition> </logic:notPresent>
tiles:definition 標籤訂義了一個ComponentDefinition(org.apache.struts.tiles.ComponentDefinition) 類型的 JavaBean。 ComponentDefinition 具備用於傳遞給它的全部屬性的 getter 和 setter 方法。logic:notPresent 標籤經過在定義以前檢查它是否已經在範圍中,從而確保 ComponentDefinition 對每一個應用程序僅建立一次。
注意: 默認設置可能會帶來麻煩。
注意您還要爲內容和標題定義默認參數。然而,這被認爲是很糟糕的作法。爲何這樣說呢?若是有人忘了使用這個標題,他們將取得默認值。因爲標題應該隨每一個頁面而改變,您不該該爲它定義默認值。那樣的話,若是有人忘了傳遞這個標題,tile 佈局就會失敗。爲了使 tile 在這種狀況下失敗,您須要作如下兩件事情:
Tile 定義的使用相似於直接使用 tile 佈局。惟一的區別:您將指定定義而不是指定 tile 佈局 JSP 頁面,而且您將使用 tiles:put 傳入更少的參數。
要使用 tile 定義,請執行如下步驟:
下面是一個使用 tile 定義的例子(index2.jsp):
<%@ taglib uri="/WEB-INF/struts-tiles.tld" prefix="tiles" %> <jsp:include page="siteLayoutDefinition.jsp"/> <tiles:insert beanName="siteLayoutDef" beanScope="application"> <tiles:put name="title" type="string" value="Get Rick Hightower Stock Quote 2" /> <tiles:put name="content" value="indexContent2.jsp"/> </tiles:insert>
注意代碼中使用了 beanName 屬性 siteLayoutDef 來指定上一節定義的定義。beanName 屬性值對應於上一節中的 bean 定義的 id 屬性值。請注意例子中使用 tiles:put 來指定兩個參數而不是四個參數,這意味着更少的鍵入工做和維護更少的代碼 ―― 這就是 DRY 的樂趣。
您看到這裏存在的問題了嗎?您必須爲定義建立一個 JSP 頁面(siteLayoutDefinition.jsp),爲內容建立一個頁面(indexContent.jsp),爲 index.jsp 自己建立一個頁面,爲佈局建立一個頁面(siteLayout.jsp),爲頁眉建立一個頁面,以及爲頁腳建立一個頁面。吆! 您總計要建立 6 個而不是 1 個 JSP 頁面(而這還只是一個 簡單 的例子)。就算您得到了可重用性,但這是以犧牲簡單性爲代價的。
關於這個例子的另外一個難以想象之處在於該定義自己。JSP 頁面的本意是爲了以文檔爲中心的方式表達可視內容。然而,該定義沒有任何內容本質上是可視的。事實上,它主要不過就是配置。一個佈局可能有多組定義,所以您會發現每一個定義都有一個 JSP 頁面真是一件麻煩事情。將全部配置保留在單個位置很不錯,可是如何作到這點呢?
注意:對定義使用 jsp:include 而不是 @page include。
若是之前使用過 tile,您也許已經看到過使用包含指令(@page include)而不是使用動態包含操做(jsp:include)的例子。 我更喜歡 jsp:include,由於包含指令在編譯時執行,並且,除非包含它的頁面改變了,不然新的 JSP 定義就不會從新定義。使用 jsp:include 操做吧,它會幫您省去一些開發方面的麻煩。事實證實二者之間的性能差異是能夠忽略的(指令要稍微快一點),可是過期的 JSP 定義的痛苦令人們躲避去使用它。
XML 定義解決了非可視化的 JSPpage 暴露的問題。與其爲每一個 JSPpage 定義一個定義,您能夠在單個配置文件中定義全部配置。然而在可以開始使用 XML 定義以前,您須要首先使用對應的 Struts 的 Tiles 插件:
<plug-in className="org.apache.struts.tiles.TilesPlugin" > <set-property property="definitions-config" value="/WEB-INF/tiles-defs.xml" /> <set-property property="moduleAware" value="true" /> <set-property property="definitions-parser-validate" value="true" /> </plug-in>
您須要向 struts 配置文件添加上述代碼。注意 definition-config 屬性指定了將包含基於 XML 的定義的 XML 文件。這段代碼還指定這個 tile 引擎是模塊感知的(module-aware),並指定它驗證 XML 文件:
一旦定義了該插件,建立 XML 定義就變得容易了。 您只需在 tile 定義文件(例如 tiles-def.xml)中添加另外一個條目:
<tiles-definitions> <definition name="siteLayoutDef" path="/siteLayout.jsp"> <put name="title" value="Rick Hightower Stock Quote System" /> <put name="header" value="/header.jsp" /> <put name="footer" value="/footer.jsp" /> <put name="content" type="string"> Content goes here </put> </definition>
根元素是 tiles-definition; ―― 這個模塊的全部 tile 定義都將定義在 tiles-definition 元素內。
definition 元素指定一個 tile 定義。上面定義的定義在功能上等價於前面定義的 JSP 版本。注意該定義的屬性稍有區別:使用 name 而不是 id,以及使用 path 而不是 page。(很氣人,不是嗎?)若是您知道如何定義一個基於 JSP 的定義,那麼定義基於 XML 的定義將證實只是小孩子玩的遊戲,由於它們在形式和功能上幾乎是徹底相同的。
如今已經定義好了 XML 定義,您須要更改 quote.jsp 和 index.jsp 以使用它。事實證實該定義的使用和之前幾乎沒有區別:惟一的區別是傳遞給 tiles:insert 標籤的屬性,以下所示(index3.jsp):
<%@ taglib uri="/WEB-INF/struts-tiles.tld" prefix="tiles" %> <tiles:insert definition="siteLayoutDef"> <tiles:put name="title" type="string" value="Get Rick Hightower Stock Quote 3" /> <tiles:put name="content" value="indexContent3.jsp"/> </tiles:insert>
注意您如今使用 definition 屬性來指定 tile 定義文件(tiles-def.xml)中建立的定義,而不是使用 beanName 和beanScope。還要注意您須要在定義 JSP 中使用 jsp:include 或 logic:notPresent。
一旦您轉變思路,開始使用 XML 定義而不是使用 JSP 定義,那麼 tile 的使用將變得更容易一些。您將只需編寫更少的代碼和維護更少的非可視化 JSP 頁面。
1. 高級 tile 主題
記住 Tiles 框架定義了一個稱爲「tile 範圍」的附加範圍,它與頁面範圍相似。像頁面範圍同樣,tile 範圍比請求範圍更私有。Tile 範圍容許 tile 用戶給 tile 傳遞變量(稱爲參數)。本質上,它使得頁面像函數同樣可調用。
記住,jsp:include 容許您調用一個頁面,同時傳遞給它一個請求參數(jsp:param)。tiles:insert 標籤相似jsp:include,不過前者更強大。 tiles:insert 標籤容許您調用一個頁面,同時向它傳遞子頁面(稱爲 tile)和屬性。Tile 範圍本質上容許您傳遞僅對該 tile 佈局可用的變量。
若是知道 tile 範圍是如何實現的,您就可以理解它。我曾建立過一個名爲 listTileScope 的調試實用程序,它容許我打印出 tile 範圍中的變量,以下面的代碼片斷所示:
import org.apache.struts.taglib.tiles.ComponentConstants; import org.apache.struts.tiles.ComponentContext; public static void listTileScope(PageContext context) throws JspException, IOException { JspWriter out = context.getOut(); ComponentContext compContext = (ComponentContext)context.getAttribute( ComponentConstants.COMPONENT_CONTEXT, PageContext.REQUEST_SCOPE); out.println("--- TILE Attributes --- <br />"); if (compContext!=null){ Iterator iter = compContext.getAttributeNames(); while(iter.hasNext()){ String name = (String)iter.next(); Object value = compContext.getAttribute(name); printNameValueType(name, value, out); } }else{ out.println("---TILE Attributes NOT FOUND---<br />"); } out.println("--------------------------- <br />"); } private static void printNameValueType( String name, Object value, JspWriter out) throws IOException{ if (value !=null){ out.println( name + " = " + value + " type (" + value.getClass().getName()+ ") " + "<br /><br />"); }else{ out.println(name + " = " + value + "<br /><br />"); } }
注意 ComponentContext 類實現了 tile 範圍。 ComponentContext 類位於 ComponentConstants.COMPONENT_CONTEXT 鍵下面的請求範圍中。這種 tile 機制確保每一個 tile 得到它本身的組件環境。
嵌套的 tile 不會和它們的父親共享相同的 tile(我費了好大的勁才瞭解到這點)。當前 tile 的 tile 範圍已在顯示嵌套的 tile 以前獲得保存。在嵌套的 tile 結束以後,父親的 tile 範圍將恢復到請求中。這個神奇的特性是在 InsertTag (org.apache.struts.taglib.tiles.InsertTag) 類的嵌套類 InsertHandler 中實現的。
到目前爲止,您已經向對應於子 tile 的 tile 佈局傳遞過屬性或傳遞簡單的字符串。能夠將您但願的任意 bean 類型做爲屬性傳入 tile 佈局。而後在那個 tile 佈局內使用該屬性。
假設您的應用程序具備這樣一個操做,它將一個 User對象放入會話範圍,或許是在用戶登陸系統以後:
public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { // Default target to success String target = new String("success"); // If login successful. UserDomainObject user = new UserDomainObject(); ... request.getSession().setAttribute("user", user); return (mapping.findForward(target)); }
接下來您將該用戶傳遞到您正在插入的 tile。這個例子將使用您在 tile 佈局(siteLayout2.jsp)內所使用的那個 tile:
<tiles:insert attribute="header" ignore="true"> <tiles:put name="title" beanName="title" beanScope="tile"/> <tiles:put name="user" beanName="user" beanScope="session"/> </tiles:insert>
該 tile 佈局經過指定 session 的範圍和 user 的 bean 名稱,從而將 userbean 傳遞給頁眉 tile。使用這項技術,您能夠在任何 JSP 範圍中將任意 bean 傳遞給 tile 或 tile 佈局,這樣該 tile 範圍就變成了另外一個範圍。這與之前並無什麼不一樣。
爲了在 header.jsp 中使用這個 userbean,可把它從 tile 範圍複製到一個其餘 bean 可以理解的範圍。這可使用tiles:useAttribute 標籤來實現。 tiles:useAttribute 標籤相似於 jsp:useBean 操做,只不過僅適用於 tile 範圍(header2.jsp):
<tiles:useAttribute id="user" name="user" classname="rickhightower.UserDomainObject" />
所以 tiles:useAttribute將把 user 對象從 tile 範圍複製到頁面範圍。一旦 bean 獲得定義,您就可以像使用頁面範圍中定義的任何 bean 同樣使用它:
<bean:write name="user" property="userName"/>
接下來,讓咱們看一下新的 header2.jsp文件的完整清單:
<%@ taglib uri="/WEB-INF/struts-tiles.tld" prefix="tiles" %> <%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %> <center> <table> <tr> <tiles:useAttribute id="user" name="user" classname="rickhightower.UserDomainObject" /> <td width="33%" bgcolor="#36566E"> <div align='left'> <font size="1" color="orange"> currently logged in as <bean:write name="user" property="userName"/> </font> </div> </td> <td width="33%"> <font color="#36566E"> <tiles:getAsString name="title" ignore="true"/> </font> </td> <td width="33%" bgcolor="#36566E"> <div align='left'> <font size="1" color="white"> <blockquote> <bean:write name="user" property="firstName"/> <br /> <bean:write name="user" property="lastName"/> <br /> </blockquote> </font> </div> </td> </tr> </table> </center>
能夠看到,頁眉如今顯示了關於當前登陸站點的用戶信息 ―― 這是一個強大的特性。能夠建立專門用於顯示域對象的 tile,而後在應用程序的許多部分重用那些 tile。考慮到這點,弄清爲何人們原先考慮將 Tiles 框架稱爲「組件」就很容易了:您事實上可以建立顯示組件。與自定義標籤(JSP 2.0 以前的版本)不一樣,這些組件全都是在 JSP 頁面中建立的。
2. 列表
常常會遇到必須傳遞多個參數的狀況。例如,您可能想要傳遞一個參數列表,以顯示 tile 佈局的導航區域中的連接。
回顧前面的「僱員清單」例子,您可能有一個 Web 應用程序須要顯示公司的分公司、部門和僱員。當在僱員名單視圖中時,employeeListing.jsp 將處於 tile 佈局的內容區域,而當前分公司的部門連接將處於導航區域中。當用戶單擊某個部門連接時,該部門的僱員的新名單將會顯示出來。當在部門視圖中時, deptListing.jsp 將處於 tile 佈局的內容區域,而當前公司的分公司連接列表將處於導航區域中。當用戶單擊某個分公司連接時,新的部門名單就會顯示出來。所以,每一個頁面(employeeListing.jsp 和 deptListing.jsp)都將傳入一個新的連接列表。您可使用putList 來完成這個任務。
Tile 容許用戶使用 putList 子元素傳入連接 ―― 適合於在 XML 和 JSP 定義中使用,或在對定義的調用或 JSP 中的 tile 佈局中使用。
假設您想使用一組標準導航連接做爲站點佈局。能夠在 tile 配置文件中使用 putList 子元素來指定這樣的連接,以下所示(tiles-def.xml):
<definition name="siteLayoutDef3" path="/siteLayout3.jsp"> <put name="title" value="Rick Hightower Stock Quote System" /> <put name="header" value="/header2.jsp" /> <put name="footer" value="/footer.jsp" /> <put name="content" type="string"> Content goes here </put> <putList name="items" > <item value="Home" link="/index.html" /> <item value="Wiley" link="http://www.wiley.com" /> <item value="Trivera Technologies" link="http://www.triveratech.com/" /> <item value="Virtuas" link="http://www.virtuas.com/" /> <item value="Rick Hightower" link="http://www.rickhightower.com" /> <item value="Rick's Blog" link="http://rickhightower.blogspot.com/" /> </putList> </definition>
putList 元素容許您指定與連接相關聯的項的一個列表。在上面的清單中,putList 定義了六個連接。
items 列表(java.util.List)被放入 tile 範圍。名稱 items 使用 putList 元素的 name 屬性來設置。
item 元素經過把 org.apache.struts.tiles.beans.MenuItem 的一個實例插入該列表來定義一個連接。value 屬性對應於連接上的標籤(label),而 link 則指向連接的 URL。
圖標與工具提示
item 元素還有爲連接指定工具提示和圖標的元素。經過查看 Struts 源代碼中的 DTD (tiles-config_1_1.dtd),能夠了解有關 item 元素和 putList 的更多內容。
要使用這種連接列表,必須修改 tile 佈局,以下所示(siteLayout3.jsp):
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %> <%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %> <%@ taglib uri="/WEB-INF/struts-tiles.tld" prefix="tiles" %> <%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %> <tiles:importAttribute /> <html> <head> <logic:present name="title"> <title> <tiles:getAsString name="title" ignore="true"/> </title> </logic:present> </head> <body> <table width="500" border="0" cellspacing="0" cellpadding="0"> <tr bgcolor="#36566E"> <td height="68" width="70%"> <div align="left"> <img src="images/hp_logo_rickhightower.gif" width="220" height="74"> </div> </td> </tr> <tr> <td height="68" width="2000"> <tiles:insert attribute="header" ignore="true"> <tiles:put name="title" beanName="title" beanScope="tile"/> <tiles:put name="user" beanName="user" beanScope="session"/> </tiles:insert> </td> </tr> <table> <tr> <td width="50%"> <ul> <logic:iterate id="item" name="items" type="org.apache.struts.tiles.beans.MenuItem" > <li> <bean:define id="link" name="item" property="link" type="java.lang.String"/> <logic:match name="link" location="start" value="/" > <html:link page="<%=link%>" > <bean:write name="item" property="value"/> </html:link> </logic:match> <logic:notMatch name="link" location="start" value="/" > <html:link href="<%=link%>"> <bean:write name="item" property="value"/> </html:link> </logic:notMatch> </li> </logic:iterate> </ul> </td> <td width="50%"> <div align="center"> <tiles:insert attribute="content"/> </div> </td> </tr> </table> <tr> <td> <tiles:insert attribute="footer" ignore="true"/> </td> </tr> </table> </body> </html>
特別要注意在列表上進行迭代的代碼段:
<ul> <logic:iterate id="item" name="items" type="org.apache.struts.tiles.beans.MenuItem" > <li> <bean:define id="link" name="item" property="link" type="java.lang.String"/> <logic:match name="link" location="start" value="/" > <html:link page="<%=link%>" > <bean:write name="item" property="value"/> </html:link> </logic:match> <logic:notMatch name="link" location="start" value="/" > <html:link href="<%=link%>"> <bean:write name="item" property="value"/> </html:link> </logic:notMatch> </li> </logic:iterate> </ul>
後面會對此進行簡化。
tiles:importAttribute 標籤將 tile 範圍中的屬性導入到頁面範圍。它相似於 tiles:useAttrribute 標籤,但它更接近獵槍而不是解剖刀。它是懶散的、骯髒的和便宜的;我一直用它(這說明了我什麼呢?)。這有效地將條目列表從 tile 範圍拷貝到頁面範圍。
注意: tiles:importAttribute 可拷貝到任何指定的範圍。
默認狀況下,tiles:importAttribute 將全部這些屬性拷貝到頁面範圍。你也能夠經過使用範圍屬性將這些屬性拷貝到其餘範圍。
一旦條目列表在頁面範圍中,您就可使用標準 Struts 標籤訪問它,以下所示(siteLayout3.jsp):
<logic:iterate id="item" name="items" type="org.apache.struts.tiles.beans.MenuItem" > ... </logic:iterate>
注意使用 logic 標籤實現的邏輯用於顯示連接。能夠檢查連接是否以「/」開始,從而肯定連接是不是相對的。若是連接 是 相對的,使用 html:link 標籤的 page 屬性。不然,若是連接指向絕對 URL 的話,使用 html:link 標籤的href 屬性,以下所示(siteLayout3.jsp):
<bean:define id="link" name="item" property="link" type="java.lang.String"/> <logic:match name="link" location="start" value="/" > <html:link page="<%=link%>" > <bean:write name="item" property="value"/> </html:link> </logic:match> <logic:notMatch name="link" location="start" value="/" > <html:link href="<%=link%>"> <bean:write name="item" property="value"/> </html:link> </logic:notMatch>
如您所想到的那樣,您可能想要使用這一顯示邏輯來在不止一個位置顯示菜單項。這就是說,您可能想要在這個頁面的範圍以外重用它。在稍後部分,您將看到如何經過將一個 tile 佈局嵌套進另外一個 tile 佈局來實現這一點。
除了能向 tile 定義中的列表添加條目以外,還可使用 tiles:putList 元素和它的 tiles:add 子元素向 JSP 中的列表添加條目(index6.jsp):
<%@ taglib uri="/WEB-INF/struts-tiles.tld" prefix="tiles" %> <%@ page import="org.apache.struts.tiles.beans.SimpleMenuItem" %> <tiles:insert definition="siteLayoutDef4"> <tiles:put name="title" type="string" value="Get Rick Hightower Stock Quote6" /> <tiles:put name="content" value="indexContent5.jsp"/> <tiles:putList name="items" > <jsp:useBean id="item1" class="SimpleMenuItem"/> <jsp:setProperty name="item1" property="link" value="/index.html"/> <jsp:setProperty name="item1" property="value" value="Home" /> <tiles:add beanName="item1"/> </tiles:putList> </tiles:insert>
上面的清單使用 jsp:useBean 來建立 SimpleMenuItem 的實例。而後使用 jsp:setProperty 來設置 SimpleMenuItembean 的連接和值屬性。最後,使用 tiles:add 將這個 bean 添加到列表中。
在上面的例子中,添加了一個 SimpleMenuItem,它細分了 tile 佈局使用的 MenuItem。然而,您能夠添加任何 bean 類型。
注意: 在 XML 中添加任何類型的 bean。
要在 tiles XML 定義中添加任何類型的 bean,可以使用 putList 的子元素 bean。這個 bean 元素帶有一個 id 和classtype。對於簡單類型,您也可使用 putList 的 add 子元素。請參閱 tiles configuration DTD (tiles-config_1_1.dtd) 以獲取更多信息。
1. 高級定義概念
幾個 JSP 頁面常用相同的默認參數。其餘頁面也使用相同的 tile 佈局但使用不一樣的 tile 參數。無需再定義一個徹底不一樣的定義,一個定義能夠擴展另外一個定義。extends 屬性讓一個定義擴展另外一個定義。
下面是一個例子:
<definition name="siteLayoutDef3" path="/siteLayout3.jsp"> <put name="title" value="Rick Hightower Stock Quote System" /> <put name="header" value="/header2.jsp" /> <put name="footer" value="/footer.jsp" /> <put name="content" type="string"> Content goes here </put> <putList name="items" > <item value="Home" link="/index.html" /> <item value="Wiley" link="http://www.wiley.com" /> <item value="Trivera Technologies" link="http://www.triveratech.com/" /> <item value="Virtuas" link="http://www.virtuas.com/" /> <item value="Rick Hightower" link="http://www.rickhightower.com" /> <item value="Rick's Blog" link="http://rickhightower.blogspot.com/" /> </putList> </definition> <definition name="siteLayoutDef4" extends="siteLayoutDef3"> <put name="title" value="Rick Hightower Quote Sub System" /> <putList name="items" > <item value="Home" link="/index.html" /> <item value="Wiley" link="http://www.wiley.com" /> <item value="Trivera Technologies" link="http://www.triveratech.com/" /> <item value="Virtuas" link="http://www.virtuas.com/" /> </putList> </definition> <definition name="siteLayoutDef5" extends="siteLayoutDef4"> <putList name="items" > </putList> </definition> <definition name="siteLayoutDef6" path="/siteLayout4.jsp" extends="siteLayoutDef4"> </definition>
注意 siteLayoutDef4 擴展了 siteLayoutDef3,覆蓋了標題值,並定義了一個更短的導航列表。它從所覆蓋的siteLayoutDef4 繼承了全部其餘參數,即頁眉、頁腳和內容。此外,注意 siteLayoutDef5 也擴展了siteLayout4,只是它清空了條目列表。一個定義繼承了它的上層定義及更上一層定義(依此類推無限制)的全部屬性。
除了覆蓋屬性以外,還可改變 tile 佈局 JSP。看看 siteLayoutDef6,它擴展自 siteLayoutDef5,並指定了一個新的 tile 佈局(siteLayout4.jsp)。
一個 tile 佈局能夠插入到另外一個 tile 佈局中,依此類推。實際上,建立的 tile 佈局如此之小,以致於它們自己並非真正的模板。相反,它們是更相似於自定義標籤的小型可視組件,而不是頁面模板。
記住您實現的邏輯用於顯示一條連接。能夠檢查連接是否以「/」開始,從而肯定連接是不是相對的,而後再正確地顯示它。若是想要在應用程序的多個地方使用同一例程,須要建立一個可視組件。
可視組件只是另外一種 tile 佈局。tile 佈局是一個可視組件仍是一個模板,只取決於您的觀點(旁觀者清)。下面的 tile 佈局定義了一個可視組件,用於顯示一個連接(linkLayout.jsp):
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %> <%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %> <%@ taglib uri="/WEB-INF/struts-tiles.tld" prefix="tiles" %> <%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %> <tiles:useAttribute id="item" name="item" classname="org.apache.struts.tiles.beans.MenuItem" /> <bean:define id="link" name="item" property="link" type="java.lang.String"/> <logic:match name="link" location="start" value="/" > <html:link page="<%=link%>" > <bean:write name="item" property="value"/> </html:link> </logic:match> <logic:notMatch name="link" location="start" value="/" > <html:link href="<%=link%>"> <bean:write name="item" property="value"/> </html:link> </logic:notMatch>
這種方法與 JSP Custom 標籤相比,容許您使用其餘自定義標籤。另外,與 Java 類(好比 Custom 標籤)相比,它是一個以文檔爲中心的 JSP,這使得使用 HTML 標籤和自定義標籤更容易。
注意: JSP 2.0 標籤文件。
您可能認識到 JSP 2.0 及其後續版本中 JSP 標籤文件的 tile 佈局的許多優勢。若是您使用的 JSP 版本太老,不支持標籤文件,那麼您如今就可使用這種技術。然而,如您很快就要看到的那樣,按個人觀點,Tiles 框架更好地實現了控制器與視圖的分離。
一旦定義了可視組件,就應該爲它建立一個定義,以下所示:
<definition name="linkLayoutDef" path="/linkLayout.jsp"> </definition>
如今您已經定義好這個定義,經過使用 tiles:insert 標籤 ,您能夠在任何頁面使用這個可視組件。甚至能夠在另外一個 tile 中使用這個可視組件。下面的代碼示例展現了在前面定義的 tile 佈局中使用這個可視組件 (siteLayout4.jsp)。
<td width="50%"> <ul> <logic:iterate id="item" name="items" type="org.apache.struts.tiles.beans.MenuItem" > <li> <tiles:insert definition="linkLayoutDef"> <tiles:put name="item" beanName="item" beanScope="page"/> </tiles:insert> </li> </logic:iterate> </ul> </td>
上面的代碼在條目列表上進行迭代,而後調用 tiles:insert,將當前條目傳遞給可視組件 (linkLayoutDef)以用於顯示。可視組件知道如何顯示一個域對象(一個菜單項)。若是您以爲本身須要再三重複編寫相同的 JSP 代碼,就應該考慮使用 tile 佈局編寫一個可視組件了。
上面的例子顯式地調用定義好的可視組件。若是您使用的 tile 佈局根據幾個因素而變化該怎麼辦呢?(即這個用戶是否登陸,他是否處於某個特定的角色,您位於站點的哪一個部分)。在這種狀況下,將 tile 做爲一個參數傳遞將是個好主意。
使用 put 元素能夠完成這件事,以下所示(tiles-def.xml):
<definition name="link.layout.def" path="/linkLayout.jsp"> </definition> <definition name="siteLayoutDef7" path="/siteLayout5.jsp" extends="siteLayoutDef4"> <put name="title" value="Rick Hightower Quote System 9" /> <putList name="items" > </putList> <put name="linkDisplay" value="link.layout.def"/> </definition>
注意 siteLayoutDef7 的 linkDisplay 屬性的值等於 link.layout.def。如今在 tile 佈局(siteLayout5.jsp)的內部,您能夠指定 linkDisplay屬性,而不是明確地調用一個特殊的 tile 佈局定義:
<ul> <logic:iterate id="item" name="items" type="org.apache.struts.tiles.beans.MenuItem"> <li> <tiles:insert attribute="linkDisplay"> <tiles:put name="item" beanName="item" beanScope="page"/> </tiles:insert> </li> </logic:iterate> </ul>
這樣的話,您的站點佈局不知道它所使用的是哪一種可視組件。經過切換站點佈局使用哪種可視組件,您能夠經過編程切換佈局部分的顯示方式。
若是您以爲須要向 tile 佈局中放入太多的 Java 代碼,或者必須在每一個指向使用特定 tile 佈局的頁面的操做中放相同的 Java 代碼,那麼應該使用 tile 控制器。在使用 controllerClass屬性插入 tile 以前,您能夠指定一個進行調用的控制器類:
<%@ taglib uri="/WEB-INF/struts-tiles.tld" prefix="tiles" %> <tiles:insert definition="siteLayoutDef5" controllerClass="rickhightower.SimpleController"> <tiles:put name="content" value="indexContent5.jsp" /> </tiles:insert>
控制器類相似於一個操做。在控制器中,能夠將模型對象映射到某個範圍中,以便 tile 可以顯示條目。
要編寫一個 tile 控制器,必須執行如下操做:
下面的清單展現了實現一個控制器的方法(rickhightower.SimpleController):
package rickhightower; import java.io.IOException; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.struts.tiles.ComponentContext; import org.apache.struts.tiles.Controller; import org.apache.struts.tiles.beans.MenuItem; import org.apache.struts.tiles.beans.SimpleMenuItem; import java.util.ArrayList; import java.util.List; /** * @author rhightower */ public class SimpleController implements Controller{ private MenuItem createMenuItem(String label, String link){ SimpleMenuItem item = new SimpleMenuItem(); item.setLink(link); item.setValue(label); return item; } private List getLinks(){ List list = new ArrayList(); list.add(createMenuItem("Home", "/index.html")); list.add(createMenuItem("Rick's", "http://www.rickhightower.com")); list.add(createMenuItem("Trivera", "http://www.triveratech.com")); return list; } /* (non-Javadoc) * */ public void perform(ComponentContext context, HttpServletRequest request, HttpServletResponse response, ServletContext servletContext) throws ServletException,package rickhightower; import java.io.IOException; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.struts.tiles.ComponentContext; import org.apache.struts.tiles.Controller; import org.apache.struts.tiles.beans.MenuItem; import org.apache.struts.tiles.beans.SimpleMenuItem; import java.util.ArrayList; import java.util.List; /** * @author rhightower */ public class SimpleController implements Controller{ private MenuItem createMenuItem(String label, String link){ SimpleMenuItem item = new SimpleMenuItem(); item.setLink(link); item.setValue(label); return item; } private List getLinks(){ List list = new ArrayList(); list.add(createMenuItem("Home", "/index.html")); list.add(createMenuItem("Rick's", "http://www.rickhightower.com")); list.add(createMenuItem("Trivera", "http://www.triveratech.com")); return list; } /* (non-Javadoc) * */ public void perform(ComponentContext context, HttpServletRequest request, HttpServletResponse response, ServletContext servletContext) throws ServletException, IOException { List items = (List)getLinks(); context.putAttribute("items",items); } IOException { List items = (List)getLinks(); context.putAttribute("items",items); } }
注意 perform() 方法得到傳遞過來的組件上下文。組件上下文帶有 tile 範圍的屬性。將東西放進組件上下文中可將它們放進 tile 範圍中。在這個簡單的例子中,調用 getLinks,它返回一個簡單的映射到 tile 範圍的 MenuItems 列表。一個真實的例子極可能會涉及到模型――也許是一個面(facade),它與數據庫進行通訊,查找特定於登陸進系統的用戶類型的連接。
注意:使用操做做爲控制器。
您也可使用操做做爲 tile 的控制器。要完成這項任務,請指定帶有 controllerUrl 屬性的操做的路徑。
您可能還未覺察到,在您安裝 Tiles 插件時,它安裝了一個自定義請求處理程序,擴展了 Struts 處理ActionForward 的方式。所以,您應該轉到 tile 定義而不是 JSP 頁面。
假設您有一個定義相似這樣:
<definition name="main.index" extends="siteLayoutDef7"> <put name="content" value="/indexContent.jsp"/> </definition>
在您的 struts 配置文件中,您能夠定義一個 forward 以轉到 main.index定義,而不是指定一個 JSP 頁面:
<action path="/Lookup" type="rickhightower.SimpleLookupAction" name="lookupForm" input="/index.jsp"> <forward name="success" path="/quote.jsp"/> <!-- forward name="failure" path="/index.jsp"/ --> <forward name="failure" path="main.index" /> </action>
能夠轉到定義已證明是一項強大的工具,消除了 JSP 中無關的邏輯。例如,若是用戶以經理身份而不是以常規用戶身份登陸,您能夠將該用戶轉到一個定義,它定義了只有經理才能使用的特殊參數 tiles。
經理的定義能夠在常規用戶定義的基礎上進行擴展。若是 tile 佈局使用帶有 ignore 屬性的 insert 標籤的話,它們甚至可使用相同的 tile 佈局。這個操做將選擇正確的 forward。您根本無需使用 logic:* 標籤。
將邏輯從 JSP 中取出並置入控制器中,是正確方向的一步,而且使用 Tiles 框架來執行這一步是如此地容易。
2. 結束語
若是您是 Tiles 框架的初學者,而且已經閱讀了本教程,那麼您已經邁出了重要一步。在相對短的時間中,咱們介紹了:
Tiles 框架使得建立可重用頁面和可視組件更加容易。經過組裝可重用 tiles,開發人員可以構建 Web 應用程序。可使用 tiles 做爲模板或者可視組件。
在某些方面,tile 佈局更相似於一個顯示函數。首先您傳遞須要使用的 tile 佈局參數。參數能夠是簡單的字符串、bean 或者 tiles。參數是 tile 的屬性,存儲在 tile 的 tile 範圍中。對於它的一部分,tile 範圍相似於頁面範圍,比請求範圍更少見。tile 範圍容許 tile 的用戶傳遞參數(也稱爲屬性)給 tile。
定義容許您定義 tiles 的默認參數。定義可以在 JSP 或者 XML 中進行定義。定義可以擴展其餘定義,這相似於類能夠擴展另外一個類。此外,定義能夠覆蓋它所擴展的定義的一部分。
Tiles 框架包括了它本身的 RequestProcessor,以便做爲 ActionForward 處理 tile 佈局。所以若是您安裝了 Tiles 插件的話,能夠轉到 tile 定義而不是 JSP 。
若是您正在使用 Struts 而不是 Tiles,那麼您不能從 Struts 得到徹底受益,而且極可能進行沒必要要的自我重複。Tiles 框架使得建立可重用的站點佈局和可視組件變得切實可行。