Inserting dynamic content: /pages/View.jsp (revised)--------------------------------------------------------------------------------------------------------------------------- --------------- <%@ taglib uri="/tags/tiles" prefix="tiles" % ><tiles:insert page="/tiles/header.jsp"><tiles:put name="title" value ="Artimus - View Article"/><tiles:put name="subtitle" value ="View Article"/></tiles:insert><tiles:insert page="/tiles/message.jsp"/><tiles:insert page="/tiles/view.jsp"/><tiles:insert page="/tiles/navbar.jsp"/>
下面是使用 <tiles:getAsString>标记输出动态文本: Writing dynamic content with getAsString------------------------------------------- ------------------------------------------------------------------------------------------- ---- <%@ taglib uri="/tags/struts-html" prefix="html" %><%@ taglib uri="/tags/tiles" prefix="tiles" % ><HTML><HEAD><html:base/><LINK rel="stylesheet" type="text/css" href="<html:rewriteforward='baseStyle'/>"><TITLE><tiles:getAsString name="title"/></TITLE></HEAD><BODY οnlοad="document.forms [0].elements[0].focus();"><!-- OUTER TABLE --><TABLE class="outer"><TR><TD align="center"><!-- INNER TABLE -- ><TABLE class="inner"><TR><TD class="navbar" colspan="3"><tiles:getAsString name="subtitle"/></TD></TR> 如果你喜欢,可以使用<bean:write>或 <bean:message>标记取代<tiles:getAsString>.。 2.)抽取 tiles 重构的主要工作是决定页面的哪个部分是tile,移动那个片断到自己的文件中再用它来替代那个片断。 这个给出了每个详细的步骤: 选择剪切块 打开一个新文件 把这个块粘贴进来 作为一个JSP(或HTML)保存 插入引入tile声明的标签库 关闭新文件 放置<tile:insert page="/path/to/new/page"/>标签的地方片断就会引用注意:在重构前设置了Tiles Definitions的页面已经能够显示。页面被编排的不同,但内容和外观还是和以前一样的。Definition常常包含body或内容tile。一些页面可以没有自己的tile,但它们可以使用共享的tile 。在不同的情况下,你可以在一个表单中使用不同的按钮。一个页面可以包含创建记录的按钮,另一个页面可以包含更新记录的按钮。这些不同的页面可以使用含有title string和动态内容的相同Definition来创建。
抽取练习 下面是抽取过程中的注意点: 所有被tile使用的自定义标记都必须放到tile中 所有自定义标记元素的开始和结束都必须在同一个tile中 避免将HTML元素传递到另一个tile中 使用注释 考虑平衡 有效的技术1. tile能够继承HTML的资源,像CSS;但它不会继承涉及JSP的资源(标签库等)。当页面执行的时候web浏览器能解析CSS,但每个tle实际是一个单独的JSP或servlet,它需要自己调用JSP资源。 2. 如果你使用<html:html>标记,将该元素的标签就必须放置在一个tile布局的开头和结尾。这一限制不适用于HTML元素。你能够在一个header tile中打开<body>元素,在footer tile中关闭</body>。但自定义标签要生效的话,tile必须完整。JSP标签元素的开始和结束必须在同一个tile中。3. 在实际操作中,你可以决定第一个tile打开一个元素,像一个表格;第二个tile提供内容,像表格中的行;在第三个tile中关闭这个元素,这种情况是可以使用的。但你最好把许开始和结束元素放到同一个tile中,这样更加容易发现标签错误。即使只在中间的tile中提供表格的行,它也可以使用一个完全的行标签 <TR>...</TR>。4. 多使用注释,让你的代码更清晰,更加容易维护,就像下面所示的标准的JavaDoc:
<%--/*** Global page header, without automatic form select.* See headerForm.jsp for version with form select.* Imports global style sheet.* Opens HEAD, BODY, and TABLE. Another tile must close these.* @author Ted Husted* @license ASF 1.0*/--%> 5. 分离页面到tiles中的工作就像是把信息存到数据库中。你可以完全的去除多余的信息,但如果你这样做,某些部分会变得更加小,甚至引发维护和性能问题的矛盾。你可能有两3个不同的内容的header或footer文件。但如果标签要改变,显然3个文件比30或300个文件更容易修改。因此对于很多编程任务,这种折衷代价还是适用的。6. 如果你要同时修改引入的CSS和其它标签,别犹豫,创建一些能够自动完成查找替换的文件,这会让你节约很多的时间。 抽取<tiles:insert>标签到Definition 在你完成上一节(抽取练习)的步骤到你的初始页面确认还能正常工作后,你能够开始本节的内容了。这是四个步骤: 移动页面到布局文件夹中 重命名body tile 转换插入标记为一个布局和Definition 更新ActionForward1. 移动重构的页面到本地布局文件夹,例如:/tiles/layouts。被重构的页面应该变成一系列的<tile:insert>标记,而页面的原始内容放到其它tile中而减少了。2. 一个抽取出来的tiles有可能是原来页面的一个body,如果是这样,就要考虑重命名这个移动的页面,这就是说原来页面的核心内容仍然在原来的地方。如果你需要修改页面内容,只要移动或重命名文件夹后再更新一下<tile:insert>标记,这样做将使你的改动带来的影响最小化。
<tiles:insert page="/tiles/view.jsp"/>to<tiles:insert page="/pages/view.jsp"/> 3.为每个插入的标记添加一个名称属性。通常匹配JSP名称,除了表示原是内容的tile。通常也使用一个非常普通的名称,像content。拷贝重构页面的<tiles:insert>声明到这章开始定义的xml配置文件中,放在<definition>元素中,如果你使用了一些<tiles:put>元素,你可以将它们放在元素开头: <definition><tiles:insert put="title" value ="Artimus - View Article"/><tiles:insert put="subtitle" value ="View Article"/><tiles:insert name="header" page="/tiles/header.jsp"/><tiles:insert name="message" page="/tiles/message.jsp"/><tiles:insert name="content" page="/pages/view.jsp"/><tiles:insert name="navbar" page="/tiles/navbar.jsp"/></definition> 然后把 <tiles:insert>标记改为<tiles:put>,把page属性名改为value,在<definition>元素内添加name和path属性名。path属性应该是你的布局页面,name 属性对应的是你的原始页面的名称,只是将斜杠替换为点号。见下面修改后的文件: <!DOCTYPE tiles-definitions PUBLIC"-//Apache Software Foundation//DTD Tiles Configuration//EN""http://jakarta.apache.org/struts/dtds/tiles-config.dtd"><tiles-definitions><definition name=".article.view" path="/pages/tiles/layouts/Base.jsp><tiles:put name="title" value ="Artimus - View Article"/><tiles:put name="subtitle" value="View Article"/><tiles:put name="header" value="/tiles/header.jsp"/><tiles:put name="message" value="/tiles/message.jsp"/><tiles:put name="content" value="/pages/view.jsp"/><tiles:put name="navbar" value="/tiles/navbar.jsp"/></definition></tiles-definitions> 现在到布局页面中,把<tiles:insert>标记改为<tiles:get>,然后删除page属性(因为现在它是Definition 的一部分),任何的<tiles:put>标记都能变为<tiles:useAttribute>。保留name属性,但要删除value属性(因为value 也是Definition 的一部分了),操作见下: A Tiles layout page: /tiles/layouts/Base.jsp--------------------------------------- ------------------------------------------------------------------------------------------- ---------<%@ taglib uri="/tags/tiles" prefix="tiles" %><tiles:useAttribute name="title"/><tiles:useAttribute name="subtitle"/><tiles:get name="header"><tiles:get name="message"/><tiles:get name="content"/><tiles:get name="navbar"/> 4.最后,用Tiles Definition替代ActionForward中相关的JSP: <actionpath="/article/View"type="org.apache.scaffold.struts.ProcessAction"parameter="org.apache.artimus.article.FindByArticle"name="articleForm"scope="reques t"validate="false"><forwardname="success"path=".article.View"/></action> 下面的图表示Tiles ActionServlet截获Definition到布局页面的流程:titles(JSP/HTML/text) [<header fragment> <content fragment> <footer fragment>] | | | /|/ /|/ /|/request -- > definition(XML) [ <put header> <put content> <put footer>] | | | /|/ /|/ /|/ layout(JSP) [ <ge theader> <get content> <get footer>] --> response
(做的真烂!~_~)
如果你先前是直接转发到JSP,你可以用标准的框架的SuccessAction来控制路由,Definition会选择和呈现布局。在任何地方你都可以把Definition作为JSP的系统路径。如果有很多的引用要修改,你能够使用带有查找替代的编辑器自动完成它们。 在运行时,Tiles ActionServlet会截获ActionForward,再次在Tiles配置文件的Definition中检查它的路径。如果发现有一个匹配的,它会在response中包含Definition的每个tile。容器然后处理包含的每个tile,HTML tiles被容器的HTML服务实现,JSP tiles被容器的JSP服务实现。 如果ActionForward路径不是一个Definition的名称,ActionServlet会把它作为普通的URL处理。 注意:如果你重构的页面不能显示原来的内容,首先确定在tiles你已经引入了所有需要的标签库。如果标签库没有被包含,浏览器会忽略这种标签,标签就不会正常显示。如果这个没问题,创建一个新的页面,一步一步把页面放到新的页面来找出错误。你会发现一个tile的问题常常是一个元素的打开和关闭不符合规定。另一个规定是检查关于ActionMapping的输入参数的路径,你应更仔细的检查Definition的名称。在改为Tiles后如果你得到的错误为:必须为绝对路径(Path must be absolute),意思是说你试着用一个Definition作为定向的路径但它在Tiles配置文件中没有被找到。在检查完Definition后,Tiles把路径传递给父类的方法,Structs创建它作为系统路径。点号用来指明目录的相对路径,因此会提示"Path must be absolute"。 测试你的更改时,要重载应用程序以便当前的Structs和Tiles配置能够装载到内存中。 当你用<tiles:insert>重构页面并把它转变为一个Definition的步骤都处理完,通过测试后,你可以期待把其它的页面直接改为Definition了。你要做下列步骤: 拷贝一个已有的Definition ,并使用相同的布局.,再给它取个新名称。 粘贴和保存你重构页面的代码片断。 修改新的Definition,引用刚刚存储和转换的代码片断。 测试,重复上面的步骤。注意:当你第一次把一个页面块转变为一个Tiles时,也许还要为它花一些时间,因为你还要为新的tiles创建一些JSP页面,以及一些额外的内容。一旦为每个tile的JSP创建好后,只有到下次修改JSP时才会被重新创建,一切又都会恢复正常。以后如果你要编辑tile的内容,只有那个JSP会被重新编译。
使你的基础布局规范化 在上面的例子中,我们在布局文件中列出了一串<tiles:insert>标记。如果你喜欢,也可以在你的布局页面中使用HTML和JSP代码,放置顶级标记是个好主意,像<HTML>或<html:html>,以便它们不会被放入需要做其它事情tiles之中。 在下面的例子中,从header和navbar tiles中抽取了顶级元素,然后把它们放到了基本布局中。我们可以把Base.jsp重命名为Article.jsp,更清楚的表达这个tile的功能。在应用程序中的其它页面也能使用一个不同的布局。
Revised layout tile (/tiles/layouts/Article.jsp)--------------------------------------------------------------------------------------- ---------------------------------------------------<%@ taglib uri="/tags/tiles" prefix="tiles" %><%@ taglib uri="/tags/struts-html" prefix="html" %><html:html><HEAD><html:base/><LINK rel="stylesheet" type="text/css" href="<html:rewriteforward='baseStyle'/>"><TITLE>Artimus - <bean:write name="title"/></TITLE></HEAD><tiles:useAttribute name="title"/><tiles:get name="header"/><tiles:get name="message"/><tiles:get name="content"/><tiles:get name="navbar"/></BODY></html:html> 提取你的Definitions到基类或扩展类中 在将页面转换为布局和Definition时, 最后可能很容易成为这样: <definition name=".article.View" path="/tiles/layouts/Article.jsp"><put name="title" value="View Article" /><put name="header" value="/tiles/header.jsp" /><put name="messages" value="/tiles/messages.jsp" /><put name="content" value="/pages/articles/view.jsp" /><put name="navbar" value="/tiles/navbar.jsp" /></definition><definition name=".article.View" path="/tiles/layouts/Article.jsp"><put name="title" value="Search Result" /><put name="header" value="/tiles/header.jsp" /><put name="messages" value="/tiles/messages.jsp" /><put name="content" value="/pages/articles/result.jsp" /><put name="navbar" value="/tiles/navbar.jsp" /></definition> 仔细看,你会发现只有第一和第四个条目不同。一个更好的书写方法是创建一个基本的Definitions。Tiles Definition支持扩展特性,让一个Definition继承属性,在需要改变的时候重载它。这里,我们扩展了.article.Base,重载了title和content: <definition name=".article.Base" path="/tiles/layouts/Article.jsp"><put name="title" value="${title}"/><put name="header" value="/tiles/header.jsp"/><put name="message" value="/tiles/message.jsp"/><put name="content" value="${content}"/><put name="navbar" value="/tiles/navbar.jsp"/></definition><definition name=".article.View" extends=".article.Base"><put name="title" value="View Article"/><put name="content" value="/pages/article/view.jsp"/></definition><definition name=".article.Result" extends=".article.Base"><put name="title" value ="Article Search Result"/><put name="content" value="/pages/article/result.jsp"/></definition> 这样就减小了改变的影响。 有个约定,我们在第一和第四个条目(title和content)放置标记,作为子类Definition需要去重载的扩展点。如果基本的Definition被直接使用,这些标签会被直接打印出来。对Tiles来说${}标签没有特殊的意思。 另一个惯例布局JSP首字母大写,tile JSP首字母小写。这表明布局页面能被直接使用,因为它是一个完整的JSP,布局JSP就像调用方法一样来使用tile页面。但这只是一个惯例,其它的命名方式也能工作的很好。 开发的常规过程 通常会拷贝一个相似的文件,在tag.xml中创建一个新的Definition。 用存在页面、tile和其它自定义信息更新Definition的路径。 打开一个现有的页面。 删除头和尾内容,留下核心的内容和tag导入语句。 检查和修正核心内容,在Definition确认标签是否符合规定。一个tile可以打开一个元素,像<TABLE>,在另一个tile中务必要关闭它。移除所有不必要的tag引入声明。随意的添加一些注释。 更新Struts配置文件(struts-config.xml))中新Definition的路径,包括所有的input属性。 重载tag和Struts配置文件。 检查页面。 反复直至满意。首先,你可能先用一些页面来呈现应用程序的轮廓。一旦你构造好程序框架,通过页面的关联(page tree)依次完成和重构每个页面并不困难。这确保你不会失去任何东西。它也能发现以前开发过程中的一些无用的页面。
管理迁移 迁移一个应用程序为Tiles结构并不困难但也不要小看它。对项目要确保有足够的时间规划。开始少数的页面可能会花去几个小时,一旦确定了结构,添加每个页面就只需几分钟了。 如果你在网站要采用一个新的界面,最好是先把它转换成Tiles结构,再修改界面,免得同时进行两项新的任务。一旦迁移到Tiles后,改变一个界面将更快速简单。 开始迁移的最好的时间是在你知道将要对站点进行改版,而手头有没有新设计要去修改时。如果你已经把应用程序迁移到Tiles中,即使新设计最后才定下来,改变工作将非常顺利。不推荐一次做两个步骤--尤其是你第一次迁移的时候。 那么迁移的底线是什么呢?一个有25个表达页面的小型的应用程序,大约有140000 kbytes个标记代码,可能被迁移到55个tiles中,而代码减少到120000 kbytes,15%的多余代码被去除。 现在新的页面创建更加快速,容易和现有的页面保持一致。要改变整个站定的布局时,只要修改整体布局或少数几个tiles,而不是站点的每个页面了。