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 控制器,必须执行以下操作:
- 创建一个实现 org.apache.struts.tiles.Controller的类。
- 实现 perform()方法。
- 在
perform()
方法中,执行一些模型操作,并将结果映射到范围中,这样 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 框架和架构。
- 如何构建和使用 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 框架使得创建可重用的站点布局和可视组件变得切实可行。
- 下载本教程使用的源代码。有两个版本可用:一个带有 jar 文件 ,一个不带 jar 文件。
- 如果您想要使用 Tiles 和 Struts,请下载 Struts 1.1,它包括了一个 Tiles 框架。
- 如果想要单独使用 Tiles ,请访问 Tiles 网站,下载该软件或者获取其他有用信息。
- 在 官方 Tomcat 网站下载最新版的 Apache Tomcat 。
- Tomcat 5 有一些有用的附件。Sing Li 的文章详细介绍了它们,servlet filtering(developerWorks,2003 年 3 月)
- developerWorks上还有以下关于 Struts/Tiles 的文章:
- Malcolm Davis 的“Struts, an open-source MVC implementation”(2001 年 2 月),介绍了 Struts 并展示了如何管理大型 Web 站点的复杂性。
- Wellie Chao 的“Struts and Tiles aid component-based development”(2002 年 6 月),结合使用 Struts 和 Tiles 来构建 Web 应用程序。
- 在“Struttin' your stuff with WebSphere Studio Application Developer, Part 2: Tiles”中(2002 年 11 月),David Carew 展示了如何使用带有 Struts 的 Tiles 模板化框架。
- “集成 Struts、Tiles 和 JavaServer Faces” (2003 年 10 月)将三种技术的强大功能结合到一个功能强大的软件包中。
- “为 Web 服务构建 Struts 应用程序”(2003 年 12 月)将 MVC 的强大功能带进 Web 服务中。
- David Carew 的“Go-ForIt Chronicles, Part 19: Struttin' your stuff with WebSphere Studio” (2002 年 9 月),该教程将带您使用 WebSphere Studio IDE 开始构建基于 Struts 的应用程序。
- Rick Hightower 还与 James Goodwill 合著了 Mastering Jakarta Struts, 2nd edition 一书(Wrox Press)。
- Chuck Cavaness 是Programming Jakarta Struts 一书的作者,从他的书中摘录了本系列 关于 Tiles 的四部分系列文章 。
- JavaWorld 上 Prakash Malani 的“UI design with Struts and Tiles”提供了 Tiles 的介绍。
- 在 IBM developerWorks 的 Java 技术专区上可以找到数百篇关于 Java 编程的各个方面的文章。
Rick Hightower喜欢使用 Java 技术、Ant、Struts、IBM Emerging Technologies Toolkit(ETTK)以及 XDoclet。 Rick 最近担任ArcMind Inc.的 CTO,这是一家主要从事企业开发方面的顾问、咨询和培训服务的公司。作为 IBM developerWorks 的积极投稿者,Rick 已编写了 10 多个教程,涵盖从 EJB (Enterprise JavaBeans) 技术到 Web 服务以至 XDoclet 的广泛内容。
在 eBlox 工作的同时,Rick 和 eBlox 团队使用 Struts 构建了用于在线电子商店的两个框架和一个 ASP(application service provider,应用服务提供程序)。 他们远在 1.0 版发布之前就在使用 Struts。 Rick 最近帮助 Trivera Technologies 整理了一套很受欢迎的课程,这些课程讲授运行在 Tomcat、Resin EE、WebSphere Studio Application Developer 以及其他平台上的 Structs。
Rick 与 James Goodwill 合著了Mastering Struts, 2nd edition 一书(Wrox Press 出版)。他还参与编写了 Java Tools for Extreme Programming 一书(John Wiley& Sons 出版,2001 年),这本书于 2002 年在 Amazon.com 网站上连续三个月被评为“畅销软件开发书籍”。它介绍了如何把 Ant、JUnit、Cactus 等应用于 J2EE(Java 2 Platform, Enterprise Edition)开发。 Rick 还为Mastering Tomcat Development 一书(John Wiley & Sons,2002 年)撰写了两章的内容,并参与编写过其他许多出版物。
Rick 在 2003 JavaOne 开发人员大会上作了关于 EJB CMP/CMR 和 XDoclet 的演讲,在 TheServerSide.com 软件座谈会上作了关于使用 XDoclet 进行 J2EE 开发的演讲。此外,Rick 还在 JDJEdge 和 WebServicesEdge 大会上作过演讲。而且,Rick 还在 Complete Programmer Network 座谈会(跨越美国 6 个不同城市)上作了关于高级 Struts 主题的演讲。
当不在全国各地讲授 Trivera Struts 课程、在大会上作关于 Struts 的演讲,或从事 Struts 咨询的时候,Rick 喜欢在通宵咖啡馆喝咖啡,编写关于 Struts 和其他主题的文章,以及以第三人称写关于他自己的事情。
注: 以上四篇文章来自http://www.ibm.com/developerworks/cn
原文地址: http://www.ibm.com/developerworks/cn/education/java/j-tiles/index.html