Tiles 框架 (一)---Tiles入门和Tiles 框架和体系结构

入门 html

本教程所讲述的内容 java

本教程讲述如何使用 Tiles 框架来建立可重用的表示组件。(在最初建立它时,Tiles 框架被命名为 Components。 后来改变了名称是由于“components(组件)”表明了太多不一样的东西,不过原先的名称的精髓仍然获得了保留。) 然而,除了站点布局以外,使用 tile 还能作其余许多事情。例如,您能够划分表示层以更好地重用布局、HTML以及其余可视组件。 web

本教程力图揭示使用 Tiles 框架的基础,而后让您的知识再上一个台阶。当完成本教程的学习时,您将可以使用更高级的 Tiles 特性来建立可重用组件。 数据库

注意:贯穿本教程,咱们交替使用术语 tile  页面,由于任何 Web 资源均可以是 tile。 一个 tile 布局表明一种特殊类型的 tile,便可以用来在它内部放置其余 tile。 一个 tile 布局能够用做另外一个 tile 布局内的 tile。 apache

明确地说,本教程: 编程

  • 定义 Tiles 框架和体系结构。
  • 介绍 Tiles 体系结构以及它是如何与 Struts 集成的。
  • 澄清一些关键 Tiles 概念。
  • 展现如何生成 tile 以及将 tile 用做站点模板。
  • 展现如何使用 XML 和 JavaServer Pages (JSP) 中的 tile 定义。
  • 定义 tile 范围(scope)以及如何将对象移进和移出 tile 范围。
  • 使用属性列表。
  • 展现如何嵌套 tile。
  • 展现如何生成 tile 以及将 tile 布局用做小型可视组件。
  • 展现如何细分定义。
  • 为 tile 建立控制器。
  • 展现如何将 tile 用做 ActionForward

谁应该学习本教程?

若是您发现本身在每一个页面上都要编写三行相同的 JSP 代码,或者您想容易地定义复杂的模版布局,那么您就会从本教程中获益。 浏览器

本教程假设您彻底理解 Java 编程、MVC(Model-View-Controller,模型-视图-控制器)、Model 2 和 JSP 技术。虽然良好的 Struts 背景会让您从本教程中得到最大好处,不过只要您精通 JSP 编程,就应该可以理解本教程讲述的大多数内容。 tomcat


软件需求和代码安装 session

为完成本教程的学习,您将须要: 架构

  • 符合 JSP 1.一、1.2 或 2.0 版的 servlet/JSP 容器。 Apache Tomcat 3.x 或更高版本是一个优秀的选择。注意:本教程的例子是使用符合 JSP 1.2 的容器来编写的。
  • Tiles 框架。可做为Struts 1.1下载包的一部分或做为单独的组件从Tiles Web 站点得到该框架。
  • 源代码。我已提供了两个版本:一个带 jar 文件的版本和为窄带用户准备的一个不带 jar 文件的版本。 Struts 附带了一个空 war 文件 struts-blank.war(在 webapps 目录下),它说明了您须要哪些配置文件和 jar 文件,以及一般将它们放在哪里。您将对本教程中的例子代码使用相同的结构。

请参阅参考资料以了解关于这些材料和附加参考资料的信息。

Tiles 框架和体系结构

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 布局工做起来就像一个显示函数。要使用某个 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 布局

典型的 tile 布局可能为页眉、页脚、菜单和正文定义矩形区域,如图 1 所示。

图 1 所示的区域能够映射到相似图 2 所示的某个 Web 站点。

注意,只需传递正确的参数,就可以容易地从新定义这个应用程序的可重用部分。 例如,雇员清单可能使用相同的页眉和页脚,可是使用不一样的菜单和正文,同时仍然可以使用 tile 布局所定义的所有通用布局区域。 这样容许对不一样的内容重用相同的 tile 布局。 与包括 HTML 标记不一样的是,您将在标记中包括内容。

 

1. Tile 布局

构建第一个 tile 布局

若是站点可以重用相同的布局(使用 HTML 表格来实现)和图像,而没必要重复相同的 HTML 代码,这样不是很好吗?

Tile 在为站点建立共同的外观方面特别出色。话虽这样说,许多开发人员并无认识到 Tiles 在建立用 JSP 实现的可重用组件方面一样也很出色。

若是您发现本身在多个页面上重复相同的 HTML 代码,就可考虑对那些页面使用 tile 布局。相似地,若是在不一样页面上的不一样地方使用相同的 HTML 或 JSP 标签,这种情形也很适合使用 tile 来建立小型可视组件。

做为 Tiles 框架的一个例子,下面将重构一个简单的股票报价应用程序来利用 tile 布局,如图 3 所示。


示例应用程序

这个简单的示例应用程序主要包含一个股票报价页面,它具备一个接受单个参数(即股票代码)的表单(index.jsp)。 另外一个页面显示股票报价的值(quote.jsp)。

研究一下下面这两个代码清单。您将重构它们以使用各类各样的 tile 布局。

index.jsp

View Code
<%@ 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

View Code
 <%@ 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 布局

为了建立一个 tile 布局,您须要作如下事情:

  1. 找出两个页面的类似之处。
  2. 建立一个新的布局页面。
  3. 建立两个新的内容页面,它们仅包含 和 之间的不一样之处。EmployeeListing.jspDeptListing.jsp
  4. 将 tile 布局插入页面 ―― 也就是让 和 在它们的页面中插入 tile 布局,并将内容做为参数传递,同时传递其余必要的参数(好比标题)。EmployeeListing.jspDeptListing.jsp

因为找出两个页面之间的类似之处须要 HTML 布局和 Web 站点适用性方面的技能,事实证实这项工做更像一门艺术,而不是像一门科学。因为某些缘由,拥有紫色的头发和纹身是有所帮助的。若是您不这样认为,能够问个人朋友 Boo。

本教程重点集中于 Struts,而不是 HTML 布局和 Web 站点适用性方面的必要技能。 所以,您不会了解关于纹身和紫色头发方面的内容。事实上,例子中的 HTML 布局是刻意简单化的,以防分散咱们对 Tiles 框架的注意力。


建立 tile 布局

一旦找出了页面之间的类似之处(这是困难的部分),您就可以建立新的布局页面(这是容易的部分)。为了建立一个 tile 布局,您必须作如下事情:

  • 使用标签库指令将 tile 标签库导入 JSP,同时导入须要的其余任何标签库。
  • 使用字符串参数来显示像页面这样使用 标签的内容。tiles:getAsString
  • 使用 标签将 tile 插入布局的适当区域。tiles:insert
  • 使用 tiles:put 标签向内部 tile 传递任何须要的参数 ―― 这个标签是 tiles:insert 标签的子标签。

将 tile 标签库导入 JSP,同时导入须要的其余任何标签库,以下所示(siteLayout.jsp):

View Code
<%@ 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文件中的标签库:

View Code
<taglib> <taglib-uri>/WEB-INF/struts-tiles.tld</taglib-uri> <taglib-location>/WEB-INF/struts-tiles.tld </taglib-location> </taglib>

接下来使用字符串参数显示诸如页面标题之类的内容。您不只须要更改页面的内容,并且还须要更改出如今浏览器中的标题。为此,须要传入 tile 布局将要使用的标题:

View Code
<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 的参数将被放入该 tile 的标题范围(称为“标题属性”)。例如,除了让标题显示在浏览器的标题栏以外,可能还但愿该标题出如今页面的页眉区域。

header.jsp文件将完成这个任务。虽然标题变量在该 tile 布局页面范围以内,但它不在该 tile 布局所插入的 tile 的范围以内。脆弱方法每一个 tile 和 tile 布局都获取它本身的环境 ―― 也就是它本身的 tile 范围。于是,您必须像下面这样给页眉 tile 传递该 tile 变量:

View Code
<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。


这个 tile 布局的完整清单

接下来,您会看到 quote.jsp  index.jsp 将要使用的这个新布局页面(siteLayout.jsp)的完整清单:

View Code
<%@ 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 定义区域,从而为应用程序定义完整的布局。


使用 tile 布局

如今已经定义好了使用 tiles 的 tile 布局,您须要使用该布局。 index.jsp  quote.jsp 都将使用同一个布局。虽然这对两个页面来讲彷佛是大量的工做,可是对于真实的 Web 应用程序,你可能会对 20 个或更多的页面使用同一个布局。 经过这种方式,您没必要在 20 个位置重复 HTML 或包括 JSP 片段。

注意:为何不就使用 jsp:include 呢?

在适当的位置包括 JSP 片段是重用 HTML 的脆弱方法。设想一下包括相同的 5 个 JSP 片段的 20 个页面 ―― 您必须重复 100 次。

为了使用 tile,您须要执行如下步骤:

  1. 使用taglib指令导入 tile 标签库。
  2. 使用 tiles:insert标签来将 tile 布局插入当前页面。
  3. 使用 tiles:put来传递字符串参数。
  4. 使用 tiles:put 来传入参数 tile。

经过使用 tile 布局,您可以在一个位置中将站点布局所须要的整个 HTML 外部化,而后只需将它插入每一个页面。观察一下下面的例子,它显示了如何把 tile 布局插入 index.jsp

View Code
<%@ 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标签):

View Code
<tiles:insert page="/siteLayout.jsp" flush="true">

page 属性指定了上一节中定义的 tile 布局。若是 flush 属性被设置为 true,这个 tile(以及到目前为止的页面)将在页面的其他部分以前(或在缓冲区满而迫使执行刷新时)写到浏览器。

要更改 quote.jsp  header.jsp 之间的页面 tile,可以使用子标签 tiles:put

View Code
<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.jspfooter.jsp indexContent.jsp):

View Code
<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中,以下面所列出的:

View Code
 <%@ 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所作的正好就是这样:

View Code
<%@ 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 页面来建立定义是最容易的方法,由于它须要最少的配置。

要建立一个 JSP 定义,请执行如下步骤:

  1. 使用 taglib指令导入 tile 标签库。
  2. 使用 logic:notPresent标签来确保该定义仅被定义一次。
  3. 使用 tiles:definition标签来定义该定义,同时传递定义了 tile 布局的 JSP 页面以及新建立的定义的范围。
  4. 使用 tiles:put 标签订义默认参数。

在下面的清单中,siteLayoutDefinition.jsp 定义了这样一个定义,它使用 siteLayout.jsp 做为 tile 布局,并定义了页眉和页脚的默认参数(以及其余参数):

View Code
 <%@ 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 在这种状况下失败,您须要作如下两件事情:

  • 不要在定义中定义默认值。
  • 在使用 tiles:insert 标签订义 tile 布局中的区域时,不要将 ignore 设置为 true。

 使用 JSP tile 定义

Tile 定义的使用相似于直接使用 tile 布局。惟一的区别:您将指定定义而不是指定 tile 布局 JSP 页面,而且您将使用 tiles:put 传入更少的参数。

要使用 tile 定义,请执行如下步骤:

  1. 使用 taglib 指令导入 tile 标签库。
  2. 使用 jsp:include 来包含定义该定义的 JSP 页面。
  3. 使用 tiles:insert标签,不过要指定“定义 bean(definition bean)”名称和范围而不是指定 tile 布局页面。
  4. 使用 tiles:put属性来仅指定标题和内容(不指定页眉和页脚)。

 下面是一个使用 tile 定义的例子(index2.jsp):

View Code
<%@ 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 插件:

View Code
<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 定义

一旦定义了该插件,建立 XML 定义就变得容易了。 您只需在 tile 定义文件(例如 tiles-def.xml)中添加另外一个条目:

View Code
<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 tile 定义

如今已经定义好了 XML 定义,您须要更改 quote.jsp  index.jsp 以使用它。事实证实该定义的使用和之前几乎没有区别:惟一的区别是传递给 tiles:insert 标签的属性,以下所示(index3.jsp):

View Code
<%@ 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 主题

理解和使用 tile 范围

记住 Tiles 框架定义了一个称为“tile 范围”的附加范围,它与页面范围相似。像页面范围同样,tile 范围比请求范围更私有。Tile 范围容许 tile 用户给 tile 传递变量(称为参数)。本质上,它使得页面像函数同样可调用。

记住,jsp:include 容许您调用一个页面,同时传递给它一个请求参数(jsp:param)。tiles:insert 标签相似jsp:include,不过前者更强大。 tiles:insert 标签容许您调用一个页面,同时向它传递子页面(称为 tile)和属性。Tile 范围本质上容许您传递仅对该 tile 布局可用的变量。

若是知道 tile 范围是如何实现的,您就可以理解它。我曾建立过一个名为 listTileScope 的调试实用程序,它容许我打印出 tile 范围中的变量,以下面的代码片断所示:

View Code
 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 中实现的。


使用 bean 属性做为参数

到目前为止,您已经向对应于子 tile 的 tile 布局传递过属性或传递简单的字符串。能够将您但愿的任意 bean 类型做为属性传入 tile 布局。而后在那个 tile 布局内使用该属性。

假设您的应用程序具备这样一个操做,它将一个 User对象放入会话范围,或许是在用户登陆系统以后:

View Code
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:

View Code
<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):

View Code
<tiles:useAttribute id="user" name="user" classname="rickhightower.UserDomainObject" />

所以 tiles:useAttribute将把 user 对象从 tile 范围复制到页面范围。一旦 bean 获得定义,您就可以像使用页面范围中定义的任何 bean 同样使用它:

View Code
<bean:write name="user" property="userName"/>

接下来,让咱们看一下新的 header2.jsp文件的完整清单:

View Code
<%@ 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 来完成这个任务。


在 XML 中使用 putList

Tile 容许用户使用 putList 子元素传入连接 ―― 适合于在 XML 和 JSP 定义中使用,或在对定义的调用或 JSP 中的 tile 布局中使用。

假设您想使用一组标准导航连接做为站点布局。能够在 tile 配置文件中使用 putList 子元素来指定这样的连接,以下所示(tiles-def.xml):

View Code
<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 布局中的列表

要使用这种连接列表,必须修改 tile 布局,以下所示(siteLayout3.jsp):

View Code
<%@ 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>

特别要注意在列表上进行迭代的代码段:

View Code
<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

tiles:importAttribute 标签将 tile 范围中的属性导入到页面范围。它相似于 tiles:useAttrribute 标签,但它更接近猎枪而不是解剖刀。它是懒散的、肮脏的和便宜的;我一直用它(这说明了我什么呢?)。这有效地将条目列表从 tile 范围拷贝到页面范围。

注意: tiles:importAttribute 可拷贝到任何指定的范围。

默认状况下,tiles:importAttribute 将全部这些属性拷贝到页面范围。你也能够经过使用范围属性将这些属性拷贝到其余范围。

一旦条目列表在页面范围中,您就可使用标准 Struts 标签访问它,以下所示(siteLayout3.jsp):

View Code
<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):

View Code
<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 布局来实现这一点。


在 JSP 中使用 putList

除了能向 tile 定义中的列表添加条目以外,还可使用 tiles:putList 元素和它的 tiles:add 子元素向 JSP 中的列表添加条目(index6.jsp):

View Code
<%@ 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 属性让一个定义扩展另外一个定义。

下面是一个例子:

View Code
<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)。


嵌套 tiles

一个 tile 布局能够插入到另外一个 tile 布局中,依此类推。实际上,建立的 tile 布局如此之小,以致于它们自己并非真正的模板。相反,它们是更相似于自定义标签的小型可视组件,而不是页面模板。

记住您实现的逻辑用于显示一条连接。能够检查连接是否以“/”开始,从而肯定连接是不是相对的,而后再正确地显示它。若是想要在应用程序的多个地方使用同一例程,须要建立一个可视组件。


建立可视组件

可视组件只是另外一种 tile 布局。tile 布局是一个可视组件仍是一个模板,只取决于您的观点(旁观者清)。下面的 tile 布局定义了一个可视组件,用于显示一个连接(linkLayout.jsp):

View Code
<%@ 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 框架更好地实现了控制器与视图的分离。


使用可视组件

一旦定义了可视组件,就应该为它建立一个定义,以下所示:

View Code
 <definition name="linkLayoutDef" path="/linkLayout.jsp"> </definition>

如今您已经定义好这个定义,经过使用 tiles:insert 标签 ,您能够在任何页面使用这个可视组件。甚至能够在另外一个 tile 中使用这个可视组件。下面的代码示例展现了在前面定义的 tile 布局中使用这个可视组件 (siteLayout4.jsp)。

View Code
 <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

上面的例子显式地调用定义好的可视组件。若是您使用的 tile 布局根据几个因素而变化该怎么办呢?(即这个用户是否登陆,他是否处于某个特定的角色,您位于站点的哪一个部分)。在这种状况下,将 tile 做为一个参数传递将是个好主意。

使用 put 元素能够完成这件事,以下所示(tiles-def.xml):

View Code
<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 布局定义:

View Code
<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 以前,您能够指定一个进行调用的控制器类:

View Code
<%@ 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 控制器,必须执行如下操做:

  1. 建立一个实现 org.apache.struts.tiles.Controller的类。
  2. 实现 perform()方法。
  3.  perform() 方法中,执行一些模型操做,并将结果映射到范围中,这样 tile 就能使用它

下面的清单展现了实现一个控制器的方法(rickhightower.SimpleController):

View Code
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 属性的操做的路径。


将 tile 定义用做 ActionForward

您可能还未觉察到,在您安装 Tiles 插件时,它安装了一个自定义请求处理程序,扩展了 Struts 处理ActionForward 的方式。所以,您应该转到 tile 定义而不是 JSP 页面。

假设您有一个定义相似这样:

View Code
<definition name="main.index" extends="siteLayoutDef7"> <put name="content" value="/indexContent.jsp"/> </definition>

在您的 struts 配置文件中,您能够定义一个 forward 以转到 main.index定义,而不是指定一个 JSP 页面:

View Code
 <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 框架和架构。
  • 如何构建和使用 tile 布局做为站点模板。
  • 如何在 XML 和 JSP 中使用 tile 定义。
  • 如何在 tile 范围中移出和移入对象。
  • 如何使用属性列表。
  • 如何嵌套 tiles。
  • 如何构建和使用 tile 布局做为小型可视组件。
  • 如何细分定义。
  • 如何建立 tile 的控制器。
  • 如何使用 tile 做为一个 ActionForward

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 框架使得建立可重用的站点布局和可视组件变得切实可行。

3. 参考资料

相关文章
相关标签/搜索