Jakarta编程:使用Tiles,第一部分
作者:Chuck Cavaness 陈正勇(zhengyongchen@21cn.com)译
本文是从《Programming Jakarta Struts》摘录的系列文章的第一部分,讲述了如何使用tiles,并对模板进行一定的了解。
到目前为止,我们还没有过多的谈及如何在struts应用程序中组织Jsp页面的内容和布局。在许多方面,这游离了struts的主题。许多优秀的书籍为如何组织web内容和页面布局提供了很好的策略。
在storefront应用程序中,我们采用了两种方式组织web页面。第一种方式,也就是通常所采用的基于jsp的方式。这可能是web设计者最熟悉的方式。Jsp页面包含表示逻辑和HTML布局标签,两者之间没有分离,这种方式通常应用于小型的、不很复杂的web应用程序。
第二种方式使用JSP的include指令。大型的web应用程序开发人员,或者是当开发人员意识到第一种方式会有多么重复的劳动之后,通常采用这种方式。如果您花了很多时间维护web应用程序之后,您就会知道,更新站点的视感是多么痛苦的一件事。使用JSP的include指令允许更多的复用,这样就能减少总的开发和维护费用。
本章所介绍的第三种方式,为您提供了一种更好的方式减少web应用程序中的冗余代码。同时您可以比前两种方法更好地分离内容和布局。
理解模板
传统的GUI工具包,如Visual Works Smalltalk和Java Swing,都提供了几种布局管理器,它们指示内容如何显示在框架或窗口中。传统的web站点在其生命周期内,布局可能会有很多或大或小的更改。使用布局管理器有助于在应用程序中封装页面的物理区域,这样当它们改变时对应用程序的其它部分影响很小。不幸的是,JSP技术本身并没有直接提供布局或布局管理器。这也是为什么会发明基于模板的方式的原因。模板并非一个新概念-它已经以其它形式存在许多年了。
为了理解在实际应用中模板如何能够简化web站点的布局。让我们与使用JSP include的方式做一下对比。目前Storefront应用程序的index.jsp 页面如例14-1所示。
例14-1: Storefront 应用程序的index.jsp页面
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %>
<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
<html:html>
<head>
<title><bean:message key="global.title"/></title>
<html:base/>
<script language=javascript src="include/scripts.js"></script>
<link rel="stylesheet" href="stylesheets/format_win_nav_main.css" type="text/css">
</head>
<body topmargin="0" leftmargin="0" bgcolor="#FFFFFF">
<!-- Header information -->
<%@ include file="include/head.inc"%>
<!-- Menu bar -->
<%@ include file="include/menubar.inc"%>
<!--- Include the special offer -->
<%@ include file="include/mainoffer.inc"%>
<!-- Featured items header row -->
<table width="645" cellpadding="0" cellspacing="0" border="0">
<tr>
<td width="21">
<html:img height="1" alt="" page="/images/spacer.gif" width="1" border="0"/>
</td>
<td width="534">
<html:img page="/images/week_picks.gif" altKey="label.featuredproducts"/>
</td>
<td width="1" bgcolor="#9E9EFF">
<html:img height="1" alt="" page="/images/spacer.gif" width="1" border="0"/>
</td>
<td width="1" bgcolor="#9E9EFF">
<html:img height="1" alt="" page="/images/spacer.gif" width="1" border="0"/>
</td>
<td width="90" bgcolor="#9E9EFF" align="right">
<html:img height="1" alt="" page="/images/spacer.gif" width="90" border="0"/>
</td>
</tr>
<tr>
<td>
<html:img height="1" alt="" page="/images/spacer.gif" width="21" border="0"/>
</td>
<td colspan="4" bgcolor="#9E9EFF">
<html:img height="1" alt="" page="/images/spacer.gif" width="1" border="0"/>
</td>
</tr>
</table>
<html:img height="10" alt="" page="/images/spacer.gif" width="1" border="0"/><br>
<!--- Include the featured items -->
<%@ include file="include/featureditems.inc"%>
<!--- Include the copyright statement -->
<%@ include file="include/copyright.inc"%>
</body>
</html:html>
虽然主页面使用了JSP的include指令,但在页面布局中仍然掺杂着内容。例如,页面显式指明了依次包含head.inc文件、menubar.inc文件和mainoffer.inc文件,紧接着copyright.inc包含在页面的底部。如果我们希望每个页面都采用这种布局,我们需要以同样的次序加入同样的语句。如果用户希望菜单在左侧而不是位于顶部,每个页面都得修改。
Storefront应用程序使用JSP include而不是直接的JSP方式。虽然include机制迈出了正确的一步,因为它的确减少了冗余(想象一下如果我们在每个页面上都包含版权信息),但它仍然没有基于模板的方式有效。
动态内容和静态内容对比
在JSP中,可以包含两种内容:静态的和动态的。Include指令如下:
<% include file=”include/copyright.inc” %>
它在编译时包含目标页面,所以不能用include指令包含运行时内容。JSP include指令将资源当作静态对象,资源上下文逐字地包含在页面中。
相比较,include行为:
<jsp:include page=”include/copyright.inc”/>
将资源作为动态对象。请求发送给资源,然后包含处理结果。模板采用动态机制,这样运行表达式可以进行求值并被包含在页面中。
什么是模板?
模板是用JSP自定义标签库描述页面布局的JSP页面。模板用于定义应用程序的页面样式,而不指定内容。运行时内容插入到模板页面中。一个或多个页面可以使用同一模板。
提示:使用模板的目的是在不用给每个页面硬编码的情况下让应用程序获得一致的视感。大多数页面会使用同一模板;但一些页面有不同的外观也不少见,因此应用程序需要多个模板。
例14-2说明了storefront应用程序的模板。
例14-2:Storefront应用程序的基本模板
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html"%>
<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean"%>
<%@ taglib uri="/WEB-INF/tiles.tld" prefix="tiles"%>
<html:html>
<head>
<title><bean:message key="global.title"/></title>
<html:base/>
</head>
<body topmargin="0" leftmargin="0" bgcolor="#FFFFFF">
<!-- Header page information -->
<tiles:insert attribute="header"/>
<!-- Menu bar -->
<tiles:insert attribute="menubar"/>
<!-- Main body information -->
<tiles:insert attribute="body-content"/>
<!-- Copyright information -->
<tiles:insert attribute="copyright"/>
</body>
</html:html>
例14-2所示的模板文件并没有引入很多的新概念。您首先要注意的就是我们使用了Struts自定义标签库。事实上我们使用了Tiles 标签库和HTML以及Bean库,这不奇怪;Tiles标签库就像其它的标签库一样,在本章的后面部分将会详细探讨Tiles标签库。
页面的其余部分是HTML布局标签的混合体。您应注意到其中没有包含内容而仅仅是insert标签,在运行时内容会插入到这里。您应该已经熟悉这里所列的Struts标签,所以我们不准备讨论它们。Insert标签起着和JSP include指令相同的作用,基本上它是说某个地方有个变量,例如叫做header,它的属性值将会传给insert标签,产生的内容将会插入到这个地方。menubar,body-content和copyright也同样地插入。我们不久将解释运行时“真正”的内容如何替代这些属性。
注意到这种布局非常类似于例14-1所示的布局。唯一的区别在于不是象例14-1那样显式的包含mainoffer和featureditem,而是模板文件包含一个body-content部分。这样允许我们在所有这种通用形式下都可复用该模板。一旦我们确定了如何提供特定页面的内容,我们可以重复使用该模板。这一个文件就可以控制多个页面的布局。如果我们要更改站点的布局,我们只需修改这一个文件-这是使用基于模板方案的真正力量。
最后一点疑惑是header、menubar、body-content和copyright部分如何组合起来形成输出。关键点在于请记住例14-2所示的JSP页面只是一个模板,你仍然需要提供特定内容的JSP页面。例如,如果我们用例14-2所示的模板改写例14-1中的index.jsp,将会如例14-3所示。
例14-3: 采用模板的Storefront应用程序的index.jsp 页面
<%@ taglib uri="/WEB-INF/tiles.tld" prefix="tiles" %>
<tiles:insert page="/layouts/storefrontDefaultLayout.jsp" flush="true">
<tiles:put name="header" value="/common/header.jsp" />
<tiles:put name="menubar" value="/common/menubar.jsp" />
<tiles:put name="body-content" value="/index-body.jsp" />
<tiles:put name="copyright" value="/common/copyright.jsp" />
</tiles:insert>
首先请注意例14-3中tiles标签库在顶部被包含。每个需要使用Tiles标签库的页面(或tile)都需要包含这一行:
<%@ taglib uri="/WEB-INF/tiles.tld" prefix="tiles" %>
例14-3使用了Tiles标签库的两个标签:insert和put(本章后面部分将会讨论所有的标签和相关的属性)。您在例14-2已经看到了insert标签,但在例14-3中它所起的作用稍微有点不同。两个属性,page和flush提供给insert标签。Page属性告知标签该页面使用了一个特定的模板(或Tiles世界里的布局-布局将在本章后面部分讲到)。我们将例14-2中的模板命名为storefrontDefaultLayout.jsp。flush属性告诉控制器在向结果页面插入内容前填充页面输出流。
例14-3中的put标签回答了前面提到的问题:特定页面的内容如何提供给模板?如同您所见到的,本例中put标签有name和value属性。如果您比较不同name属性值,您会发现它们与例14-2中的模板文件期望值相符。当例14-3的index.jsp执行时,模板文件被处理,从put标签动态的传入header.jsp、menubar.jsp,、index-tiles.jsp和copyright.jsp文件:
<tiles:insert page="/layouts/storefrontDefaultLayout.jsp" flush="true">
<tiles:put name="header" value="/common/header.jsp" />
<tiles:put name="menubar" value="/common/menubar.jsp" />
<tiles:put name="body-content" value="/index-body.jsp" />
<tiles:put name="copyright" value="/common/copyright.jsp" />
</tiles:insert>
执行过程中,put标签的值动态的代入模板文件并被处理。结果输出就是显示给客户的。
在结束模板的讨论之前,这里有另外一个页面,它也使用例14-2中的同一模板,但它提供了不同的body-content。例14-4显示了itemdetail.jsp页面。
例14-4: Storefront应用程序的itemdetail.jsp页面
<%@ taglib uri="/WEB-INF/tiles.tld" prefix="tiles" %>
<tiles:insert page="../layouts/storefrontDefaultLayout.jsp" flush="true">
<tiles:put name="header" value="../common/header.jsp"/>
<tiles:put name="menubar" value="../common/menubar.jsp"/>
<tiles:put name="body-content" value="../catalog/itemdetail-body.jsp"/>
<tiles:put name="copyright" value="../common/copyright.jsp"/>
</tiles:insert>
例14-3中的index.jsp页面和例14-4中的itemdetail.jsp页面之间唯一的不同在于body-content属性提供的内容不同。
提示:如果您还没有意识到使用模板的价值所在,请您注意例14-3和例14-4 中的index.jsp和itemdetail.jsp页面都没有指定内容该如何布局。它们都引用了storefrontDefaultLayout.jsp文件,其唯一作用就是以预描述的格式显示内容。如果我们要改变网站的布局,我们只需修改storeforntDefaultLayout.jsp文件。
在下一部分,我们将学习如何安装和配置tiles。
Chuck Cavaness毕业于Geogia Tech.,获计算机工程与科学学位,他在医疗、银行和B2B等部门开发基于java的企业系统。