Struts Tiles
我很喜歡 struts ,這是我目前最熟悉的 MVC Framework ,但是 struts 的 template Engine 和 Turbine(jakarta 另外一個(gè) mvc framework,還有一個(gè) tapestry )使 用的 Velocity 有異曲同工之妙,另外如果你們?cè)?Mail List 看到 Craig R. McClanahan 這號(hào)人物, 他就是“神”的代言人! |
|
MVCII Framework Cotroller是指由 Servlet 所主導(dǎo),Model 為 JavaBean所開(kāi)發(fā), 最后以 JSP 做 View 端的呈現(xiàn),最后 將資料返回到客戶端. 而今天我要討論的就是客戶端的 Template Engine -- Tiles. |
View (Template Engine)-Tiles
Tiles是由Cedric Dumoulin老大所開(kāi)發(fā)的 Template Engine , 什么叫做 Template Engine呢, 他是一個(gè)版面切割控制的處理中心.通常我們?cè)谠绻艜r(shí)代大約 ( 1995 ~ 2000 )年間 , 設(shè)計(jì)網(wǎng)頁(yè)大多以 Frame 為切割網(wǎng)頁(yè)的方式 , 因?yàn)楫?dāng)時(shí)網(wǎng)絡(luò)帶寬不足, 加上開(kāi)發(fā)工具短缺,所以我們那時(shí)候?qū)τ诎婷娴目刂拼?概也只是這樣, 但隨著寬帶網(wǎng)絡(luò)的普及化,造就了網(wǎng)頁(yè)的復(fù)雜功能, HTML 4.0 包含了 Layer的功能,問(wèn)題 來(lái)了, Layer 無(wú)法跨過(guò) Frame變成一個(gè)浮動(dòng)的控制小窗口,所以 Frame漸漸被淘汰,變成整個(gè)網(wǎng)頁(yè)由 Table 的切割來(lái)組合而成, 但是, Table 的設(shè)計(jì)大多屬于網(wǎng)頁(yè)美工的工作,你要他們懂得如何寫動(dòng)態(tài)程序, 大概只有 1/10 的美工可以做到,所以我們建議是各師其職,讓網(wǎng)頁(yè)視覺(jué)大師的工作就單純只是網(wǎng)頁(yè)設(shè)計(jì), 所以 Template Engine就應(yīng)運(yùn)而生,那比較有名的有, Velocity, Tiles, FreeMaker等等. 而 Struts 是使用 Tiles的,這次我就針對(duì) Tiles 做初級(jí)的介紹.
基本上, 你在撰寫 JSP的時(shí)候, 如果 /WEB-INF/lib/之下有放struts.jar那就代表說(shuō), 你的 JSP 可以 import struts 的組件進(jìn)來(lái), 而 struts-tiles.tld我通常會(huì)放在 /WEB-INF/tlds/目錄之下,所以你在 JSP 的開(kāi)始的地方就要寫
<%@ taglib uri="/WEB-INF/tlds/struts-tiles.tld" prefix="tiles" %>
這意思就是說(shuō)你這個(gè)網(wǎng)頁(yè)將會(huì)通過(guò) Struts-Tiles 這個(gè) TagLib去調(diào)用 Tiles Template Engine , 你可以自 己打開(kāi) struts-tiles.tld 這個(gè)文件看看, 里面的定義就是說(shuō),當(dāng)你調(diào)用到其中的 tag時(shí)候,他需要去調(diào) 用哪一個(gè)程序來(lái)執(zhí)行你想得到的結(jié)果.
完全戰(zhàn)略首部曲--建立模板 (template.jsp)
建立一個(gè) template.jsp, 你先規(guī)劃書(shū)面需要切割成為各個(gè)區(qū)塊,本范例是切成上方標(biāo)題區(qū)(top),左方主選單 (menu),右方主畫(huà)面再切割上下區(qū)域各為 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%\'>
?<!-- 上方標(biāo)題區(qū) -->
?<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>
?<!-- 右方主畫(huà)面 -->
?<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>
完全戰(zhàn)略二部曲--定義 definations.xml
根據(jù) template.jsp 定義的 InsertTag 屬性名稱 ( attribute )給予一個(gè) jsp/html來(lái)顯示
?<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>
完全戰(zhàn)略三部曲--制作 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 ) {
????????}
????}
}
并且設(shè)定 web.xml增加一個(gè) 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>
測(cè)試網(wǎng)頁(yè)呈現(xiàn)
當(dāng)然,你需要自己建立相關(guān)定義在 definations.xml 的 jsp文件, 接著重新啟動(dòng) tomcat, 你就可以看到 http://localhost:8080/test.screen是一個(gè)整合起來(lái)的畫(huà)面了
- 設(shè)定相關(guān)的 compile 環(huán)境, 基本上,可以直接使用 struts source 的 libs 和 sources
- 設(shè)定相關(guān)的 properties 及 xml,如果不太了解, 請(qǐng)直接查閱 oreilly 所出的 Struts
- 請(qǐng)尊重知識(shí)產(chǎn)權(quán),本文章之原始文件不得用于商業(yè)用途,需要時(shí)請(qǐng)于本公司聯(lián)絡(luò).
- Struts 網(wǎng)站: http://jakarta.apache.org/struts/
- Tiles網(wǎng)站: http://www.lifl.fr/~dumoulin/tiles/
- Tomcat 網(wǎng)站: http://jakarta.apache.org/tomcat/
- 以上程序都在 Tomcat 4.1.x以上以及 Sun JDK 1.4.x以上測(cè)試完成
單獨(dú)使用 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>
簡(jiǎn)單來(lái)說(shuō), 上一篇介紹的 tiles definitions 的方法是一對(duì)一, tiles:insert 會(huì)去找 definitions 中的 put 值, 把指向的 jsp 抓進(jìn)來(lái), 一起包裝成一個(gè)網(wǎng)頁(yè)送到客戶端的瀏覽器, 但是, 如果我希望在 template 中一次 加入多筆的頁(yè)面該怎么做呢, 哪就得用 <putList> 接著使用 iterate 把他一個(gè)一個(gè)取出來(lái)顯示.
<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, 是一個(gè) webservice 的格式, 相關(guān)的介紹有
XML.com RSS 的介紹
Oreilly RSS 研究中心
RSS 教學(xué)手冊(cè)
RSS 最新消息
基本上有幾個(gè)好處
- 可能放到各個(gè)不同的 tiles channel 中 .
- 在同一個(gè) page 可能放到好幾個(gè)不同 channel .
- 可以簡(jiǎn)單的重新繪出 channel 畫(huà)面.
- 可能符合好幾個(gè) channel , 每一個(gè)都可以各自重繪.
首先 我們先定義 tiles-definition.xml , 最重要的, 是 controllerUrl 需要設(shè)定 , 此外, 還需要得到 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>
接著建立一個(gè) 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"> 取得標(biāo)題外, 其余使用 <tiles:insert attribute="menu"> |
Menu Layout | - title
- items
| 使用 <tiles:getAsString attribute="title"> 取得標(biāo)題外, 其余使用 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> 將資料放進(jìn)來(lái) |
Center Layout | - header
- right
- body
- left
- footer
| 使用 <tiles:insert> 和 <tiles:put> 將資料放進(jìn)來(lái) |
Tabs Layout | - tabList
- selectedIndex
- parameterName
| 這個(gè)幾乎以上用到的觀念都會(huì)用到 |
當(dāng)然, 你也可以建立自己的 Layout , 我們希望你能建立符合 MVC 觀念的 Layout!!