Struts Tiles
我很喜欢 struts ,这是我目前最熟悉的 MVC Framework ,但是 struts 的 template Engine 和 Turbine(jakarta 另外一个 mvc framework,还有一个 tapestry )使 用的 Velocity 有异曲同工之妙,另外如果你们在 Mail List 看到 Craig R. McClanahan 这号人物, 他就是“神”的代言人! |
|
MVCII Framework Cotroller是指由 Servlet 所主导,Model 为 JavaBean所开发, 最后以 JSP 做 View 端的呈现,最后 将资料返回到客户端. 而今天我要讨论的就是客户端的 Template Engine -- Tiles. |
View (Template Engine)-Tiles
Tiles是由Cedric Dumoulin老大所开发的 Template Engine , 什么叫做 Template Engine呢, 他是一个版面切割控制的处理中心.通常我们在早古时代大约 ( 1995 ~ 2000 )年间 , 设计网页大多以 Frame 为切割网页的方式 , 因为当时网络带宽不足, 加上开发工具短缺,所以我们那时候对于版面的控制大 概也只是这样, 但随着宽带网络的普及化,造就了网页的复杂功能, HTML 4.0 包含了 Layer的功能,问题 来了, Layer 无法跨过 Frame变成一个浮动的控制小窗口,所以 Frame渐渐被淘汰,变成整个网页由 Table 的切割来组合而成, 但是, Table 的设计大多属于网页美工的工作,你要他们懂得如何写动态程序, 大概只有 1/10 的美工可以做到,所以我们建议是各师其职,让网页视觉大师的工作就单纯只是网页设计, 所以 Template Engine就应运而生,那比较有名的有, Velocity, Tiles, FreeMaker等等. 而 Struts 是使用 Tiles的,这次我就针对 Tiles 做初级的介绍.
基本上, 你在撰写 JSP的时候, 如果 /WEB-INF/lib/之下有放struts.jar那就代表说, 你的 JSP 可以 import struts 的组件进来, 而 struts-tiles.tld我通常会放在 /WEB-INF/tlds/目录之下,所以你在 JSP 的开始的地方就要写
<%@ taglib uri="/WEB-INF/tlds/struts-tiles.tld" prefix="tiles" %>
这意思就是说你这个网页将会通过 Struts-Tiles 这个 TagLib去调用 Tiles Template Engine , 你可以自 己打开 struts-tiles.tld 这个文件看看, 里面的定义就是说,当你调用到其中的 tag时候,他需要去调 用哪一个程序来执行你想得到的结果.
完全战略首部曲--建立模板 (template.jsp)
建立一个 template.jsp, 你先规划书面需要切割成为各个区块,本范例是切成上方标题区(top),左方主选单 (menu),右方主画面再切割上下区域各为 main 及 copyright :
<%@ page contentType="text/html;charset=BIG5" %>
<%@ taglib uri="/WEB-INF/tlds/struts-tiles.tld" prefix="tiles" %>
<BODY leftmargin="0" marginheight="0" marginwidth="0" topmargin="0" bgcolor="#FFFFFF"
link="#660000">
<table border=\'0\' cellpadding=\'0\' cellspacing=\'0\' width=\'100%\'>
<!-- 上方标题区 -->
<tr>
<td colspan=\'2\'>
<img src="<%=request.getContextPath()%>/images/top.gif" border="0">
</td>
<!-- 左方主选单 -->
<tr valign=\'top\'>
<td width=\'120\' bgcolor=\'#FFFFFF\' align=\'center\'>
<tiles:insert attribute="menu"/>
</td>
<!-- 右方主画面 -->
<td width=\'680\'>
<table border=\'0\' cellpadding=\'0\' cellspacing=\'0\' width=\'100%\'>
<tr>
<td bgcolor=\'ffffff\'>
<tiles:insert attribute="main"/>
</td>
</tr>
</table>
</td>
<tr>
<td colspan=\'2\'>
<tiles:insert attribute="copyright"/>
</td>
</table>
完全战略二部曲--定义 definations.xml
根据 template.jsp 定义的 InsertTag 属性名称 ( attribute )给予一个 jsp/html来显示
<definition name="test.screen" path="/admin/template.jsp">
<put name="menu" value="/menu.jsp"/>
<put name="main" value="/index.jsp"/>
<put name="copyright" value="/copyright.jsp"/>
</definition>
完全战略三部曲--制作 ScreenServlet.java (WARN:copyrights are reserved by Softleader Copr.)
编译以下之程序(ScreenServlet.class)放到 /WEB-INF/classes/com/softleader/system/init/之下
package com.softleader.system.init;
import java.util.StringTokenizer;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.URL;
import javax.servlet.*;
import javax.servlet.ServletException;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.tiles.*;
import org.apache.struts.tiles.TilesUtil;
public class ScreenServlet extends HttpServlet {
private ServletContext context;
/** Debug flag */
public static final boolean debug = true;
/** Associated definition factory */
protected DefinitionsFactory definitionFactory;
protected ComponentDefinition definition;
private TilesRequestProcessor trp;
public void init() throws ServletException {
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
process(request, response);
}
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
process(request, response);
}
public void process(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
// init screen
String screenName = null;
String selectedUrl = request.getRequestURI();
// get the screen name
int lastPathSeparator = selectedUrl.lastIndexOf("/") + 1;
int lastDot = selectedUrl.lastIndexOf(".");
if (lastPathSeparator != -1 && lastDot != -1 && lastDot > lastPathSeparator) {
screenName = selectedUrl.substring(lastPathSeparator);
}
try {
// Read definition from factory, but we can create it here.
//ComponentDefinition definition = DefinitionsUtil.getDefinition( screenName,
request, this.getServletContext() );
//System.out.println("get Definition " + definition );
//DefinitionsUtil.setActionDefinition( request, definition);
//DefinitionsFactory definitionsFactory =
DefinitionsUtil.getDefinitionsFactory(getServletContext());
DefinitionsFactory definitionsFactory = TilesUtil.getDefinitionsFactory(request,
getServletContext());
String uri="";
Controller controller;
ComponentContext tileContext = null;
if( definitionsFactory != null ) {
// Get definition of tiles/component corresponding to uri.
ComponentDefinition definition
= definitionsFactory.getDefinition(screenName, request, getServletContext());
if( definition != null ){
// We have a definition.
// We use it to complete missing attribute in context.
// We also get uri, controller.
uri = definition.getPath();
controller = definition.getOrCreateController();
if( tileContext == null ) {
tileContext = new ComponentContext( definition.getAttributes() );
ComponentContext.setContext( tileContext, request);
}
else
tileContext.addMissing( definition.getAttributes() );
} // end if
} // end if
RequestDispatcher rd = getServletContext().getRequestDispatcher(uri);
rd.forward(request, response);
} catch( Exception ex ) {
}
}
}
并且设定 web.xml增加一个 ScreenServlet
<servlet>
<servlet-name>ScreenServlet</servlet-name>
<display-name>ScreenServlet</display-name>
<servlet-class>com.softleader.system.init.ScreenServlet</servlet-class>
<load-on-startup>3</load-on-startup>
</servlet>
测试网页呈现
当然,你需要自己建立相关定义在 definations.xml 的 jsp文件, 接着重新启动 tomcat, 你就可以看到 http://localhost:8080/test.screen是一个整合起来的画面了
- 设定相关的 compile 环境, 基本上,可以直接使用 struts source 的 libs 和 sources
- 设定相关的 properties 及 xml,如果不太了解, 请直接查阅 oreilly 所出的 Struts
- 请尊重知识产权,本文章之原始文件不得用于商业用途,需要时请于本公司联络.
- Struts 网站: http://jakarta.apache.org/struts/
- Tiles网站: http://www.lifl.fr/~dumoulin/tiles/
- Tomcat 网站: http://jakarta.apache.org/tomcat/
- 以上程序都在 Tomcat 4.1.x以上以及 Sun JDK 1.4.x以上测试完成
单独使用 Tiles
把 tiles.jar 放到 WEB-INF/lib/
把 tiles.tld 放到 WEB-INF/
把 commons-digester.jar,commons-collections.jar,commons-beanutils.jar 放到 WEB-INF/lib/ 下
把 jakarta commons *.tld 放到 WEB-INF/ 下
接着在 WEB-INF/web.xml 中增加
<servlet>
<servlet-name>action</servlet-name>
<servlet-class>org.apache.struts.titles.TilesServlet</servlet-class>
<init-param>
<param-name>definitions-config</param-name>
<param-value>/WEB-INF/tiles/tiles-definitions.xml</param-value>
</init-param>
<init-param>
<param-name>definitions-parser-validate</param-name>
<param-value>true</param-value>
</init-param>
</servlet>
使用 <putList> 及 <add>
简单来说, 上一篇介绍的 tiles definitions 的方法是一对一, tiles:insert 会去找 definitions 中的 put 值, 把指向的 jsp 抓进来, 一起包装成一个网页送到客户端的浏览器, 但是, 如果我希望在 template 中一次 加入多笔的页面该怎么做呢, 哪就得用 <putList> 接着使用 iterate 把他一个一个取出来显示.
<titles:insert page="/template.jsp">
<tiles:putList name="items">
<tiles:add value="home"/>
<tiles:add><img
src="<%=request.getContextPath()%>/images/logo.gif"></titles:add>
<tiles:add value="documentation"/>
</titles:putList>
</titles:insert>
在 view 端 jsp 中要写
<tiles:importAttribute/>
<table>
<logic:iterate id="item" name="items">
<tr><td><%=item%></td></tr>
</logic:iterate>
</table>
RssChannel
所谓的 RssData, 是一个 webservice 的格式, 相关的介绍有
XML.com RSS 的介绍
Oreilly RSS 研究中心
RSS 教学手册
RSS 最新消息
基本上有几个好处
- 可能放到各个不同的 tiles channel 中 .
- 在同一个 page 可能放到好几个不同 channel .
- 可以简单的重新绘出 channel 画面.
- 可能符合好几个 channel , 每一个都可以各自重绘.
首先 我们先定义 tiles-definition.xml , 最重要的, 是 controllerUrl 需要设定 , 此外, 还需要得到 rss 的格式.
<definition name="examples.rssChannel.body" path="/examples/tiles/rssChannels.jsp"
controllerUrl="/examples/controller/rssChannel.do">
<putList name="urls">
<add value="http://newsforge.com/newsforge.rss"/>
<add value="http://xmlhack.com/rss.php"/>
<add value="http://lwn.net/headlines/rss"/>
</putList>
</definition>
在 strut-config.xml 中定义
<action path="/examples/controller/rssChannel"
type="org.apache.struts.example.tiles.rssChannel.RssChannelsAction">
</action>
接着建立一个 RssChannelsAction 的 Class
public final class RssChannelsAction extends TilesAction {
public static final String CHANNELS_KEY = "CHANNELS";
public static final String CHANNEL_URLS_KEY= "urls";
public ActionForward doExecute(ActionMapping mapping,
ActionForm form, HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException, Exception {
org.apache.commons.digester.rss.Channel channel = null ;
List channels = (List)context.getAttribute(CHANNEL_URLS_KEY);
List channelBeans = new ArrayList(channels.size());
for ( int i=0 ; i < channels.size(); i++ ) {
RSSDigester digester = new RSSDigester();
String url = (String)channels.get(i);
Channel obj = (Channel) digester.parse(url);
channelBeans.add(obj);
}
context.putAttribute(CHANNELS_KEY,channelBeans);
return null;
}
}
最后, 在 view 端 jsp 这样就可以看到 rssChannel 的资料啦
<%@ page language="java" %>
<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
<%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %>
<%@ taglib uri="/WEB-INF/struts-tiles.tld" prefix="tiles" %>
<div align="center"><font size="+1"><b>
<tiles:importAttribute name="CHANNELS" scope="page"/>
<logic:iterate name="CHANNELS" id="CHANNEL" >
<TABLE border="0" cellspacing="0" cellpadding="4" width="100%" align="center" >
<TR>
<TD class="spanhd" ><logic:present name="CHANNEL" property="image">
<a href="<bean:write name="CHANNEL" property="link"/>">
<img src="<bean:write name="CHANNEL"
property="image.URL"/>"></logic:present></a>
</TD>
<TD class="spanhd" width="100%"><bean:write name="CHANNEL" property="title"/>
<a href="<bean:write name="CHANNEL" property="link"/>">[home]</a></TD>
</TR>
<TD class="yellow" colspan="2"><bean:write name="CHANNEL"
property="description"/></TD>
</TR>
<TR>
<TD class="datagrey" colspan="2">
<logic:iterate name="CHANNEL" property="items" id="ITEM">
<br><b><bean:write name="ITEM" property="title"/></b>
<br><bean:write name="ITEM" property="description"/>
<br> [ <a href="<bean:write name="ITEM"
property="link"/>">more</a> ]
<br>
</logic:iterate>
</TD>
</TR>
</TABLE>
<br>
</logic:iterate>
</b></font></div>
Layouts
目前 tiles-example 有提供几种不同的 layout 可以参考
Layout Name | Parameters | Use |
---|
Class Layout | - title
- header
- menu
- body
- fotter
| 使用 <tiles:getAsString attribute="title"> 取得标题外, 其余使用 <tiles:insert attribute="menu"> |
Menu Layout | - title
- items
| 使用 <tiles:getAsString attribute="title"> 取得标题外, 其余使用 org.apache.struts.tiles.beans.MenuItem iterate |
VBox or VStack Layout | - list
| 使用 <tiles:useAttribute classname="java.util.List" name="list" id="list"> |
Multi-columns Layout | - numCols
- list1
- list2 [optional]
- list3 [optional]
- listn [optional]
| 使用 <tiles:useAttribute classname="java.util.String" name="numCols" id="numColsStr"> 接着使用 <tiles:insert> 和 <tiles:put> 将资料放进来 |
Center Layout | - header
- right
- body
- left
- footer
| 使用 <tiles:insert> 和 <tiles:put> 将资料放进来 |
Tabs Layout | - tabList
- selectedIndex
- parameterName
| 这个几乎以上用到的观念都会用到 |
当然, 你也可以建立自己的 Layout , 我们希望你能建立符合 MVC 观念的 Layout!!