掌握 Ajax,第 6 部分: 建立基於 DOM 的 Web 應用程序

假設你打算結合多種技術來構建一個企業級web站點。比如,你準備採用J2EE技術往你的web站點裏添加新內容,而這個系統的其他部分是用CGI或者微軟的IIS Server搭建的。

在這種情況下,怎樣讓你的應用系統從外觀和感受(look and feel)上保持一致呢?一種辦案就是採用J2EE技術全部重寫,然後選用一種框架,比如Struts-Tiles,但這種辦案的開發成本太高,不太現實。另一種可選方案是在你的應用系統的各個部分採用相同的Look and Feel。但這種方案會使維護站點變成噩夢,因爲每當一個應用系統裏面的Look and Feel需要改變的時候,你就需要讓系統裏的其他web應用保持同樣的改變。

大多數用於解決這種商務需求的可用框架都有一個共同的缺點,他們不是平臺相關就是框架相關。當你決定採用Tiles作爲struts修飾器的時候,需要創建tiles定義文件tiles-defs.xml,然後在struts-config.xml裏面聲明forwards,引用這些tiles以修飾原始的JSP。

最簡單的一種可能的解決方案是,全部採用純html方式來生成你的web應用,每一個html頁面都不需要知道自己將會被如何修飾,而是在外部採用某種機制來選擇合適的修飾器修飾它們。這就是SiteMesh的功能。

SiteMesh是基於Java、J2EE和XML的開源框架,依賴於從Servlet 2.3版本里引入的新功能——過濾器(Filters)

安裝和設置

按照以往的經驗,學習任何新技術或新框架最好的辦法,就是使用它來創建一個簡單的應用程序。所以,我們將使用SiteMesh來創建一個簡單的Struts應用程序。我們的應用程序包括三個頁面:

•一個登錄頁面
•一個幫助頁面,包括頁頭和頁腳
•一個主頁面,包括頁頭、頁腳和頁邊菜單

下面是創建這個簡單web應用程序的步驟:

1.SiteMesh基於過濾器,所以我們需要把SiteMesh過濾器通知給我們的web應用程序。在web.xml文件里加入如下幾行:

<filter>
<filter-name>sitemesh</filter-name>
<filter-class>
com.opensymphony.module.sitemesh.filter.PageFilter
</filter-class>
<init-param>
<param-name>debug.pagewriter</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>sitemesh</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

這幾行是告訴web容器,所有對web應用的請求都會經由PageFilter“過濾”一下。PageFilter是sitemesh-2.1.jar裏的一個類,你可以從http://www.opensymphony.com/sitemesh/download.html下載該jar包。

2.在WEB-INF目錄下生成一個decorators.xml文件,內容如下:

<decorators defaultdir="/decorators">
<!— 給需要頁邊菜單的頁面配置頁邊菜單修飾器 -->
<decorator name="sidemenu" page="sidemenu.jsp">
<pattern>/home.jsp</pattern>
</decorator>
<!— 給需要頁頭和頁腳的頁面配置頁頭頁腳修飾器 -->
<decorator name="headerfooter" page="headerfooter.jsp">
<pattern>/help.jsp</pattern>
</decorator>
</decorators>

decorators.xml文件用來在你的應用程序裏定義修飾器(decorators)。在這個文件裏,每個<decorator>元素定義一個修飾器,name指定修飾器名,page指定修飾器所使用的JSP頁面。<pattern>子元素指定這些修飾器如何應用到實際的頁面上去。

在我們的示例web應用裏,定義了兩個修飾器:追加頁頭和頁腳的headerfooter.jsp和追加頁邊菜單的sidemenu.jsp。我們想修飾help頁面追加頁頭和頁腳,所以我們追加了一個/help.jsp路徑子元素給headerfooter.jsp修飾器。

3.在WebContent/decorators目錄下創建headerfooter.jsp:

<%@ taglib
uri="http://www.opensymphony.com/sitemesh/decorator"
prefix="decorator" %>
<html>
<head>
<title>
My Site -
<decorator:title default="Welcome!" />
</title>
<decorator:head />
</head>
<body>
<table>
<tr>
<td>
<H1>
SiteMesh Corporation
<H1>
</td>
</tr>
<tr>
<td><decorator:body /></td>
</tr>
<tr>
<td> SiteMesh copyright</td>
</tr>
</table>
</body>
</html>

一個SiteMesh修飾器其實就是一個使用SiteMesh自定義標籤的JSP頁面。在我們的web應用裏,當用戶請求help頁面的時候,SiteMesh會攔截這個請求,然後再把它發送給web應用。而當應用返回響應的時候,SiteMesh會結合headerfooter.jsp文件解析這個響應,遇到<decorator:head/>就插入響應文件的<head>,遇到<decorator:body/>就插入響應文件的<body>。最後,被headerfooter.jsp修飾過的文件會被返回給客戶端。

4.在WebContent目錄下創建help.jsp:

<HTML>
<HEAD>
<%@ page
language="java"
contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"
%>
<TITLE>Help Page</TITLE>
</HEAD>
<BODY>
Help Page
</BODY>
</HTML>

這是一個在web應用裏很常見的help頁面。

5.在瀏覽器裏請求help.jsp頁面,測試SiteMesh安裝是否正常。瀏覽器將會返回一個包含頁頭和頁腳的help頁面。

SiteMesh架構

SiteMesh架構基於PageFilter——一個Servlet過濾器。容器接收到頁面請求時,會把請求傳遞給PageFilter,PageFilter收集應用程序的響應細節,生成自定義的響應對象,然後連同請求一起傳遞給web應用程序。web應用程序把響應資源寫入到自定義響應對象裏,再返回給PageFilter。

1.解析階段
當控制返回給PageFilter的時候,它會檢查web應用生成響應的內容類型(content type),然後基於響應類型,生成不同的解析器來解析響應。比如,如果應用返回text/html類型的內容,SiteMesh會生成一個FastPageParser實例,並把web應用生成的頁面傳遞給它。FastPageParser會解析這個頁面,提取出這個頁面的header、footer、title 等內容。

2.修飾階段

解析結束後,SiteMesh開始修飾頁面。這一階段分成兩部分:

a.決定如何修飾

SiteMesh有一個概念,叫做修飾器映射,實現這個概念的接口是DecoratorMapper(有init()和getDecorator()方法)。映射器在sitemesh.xml裏聲明。在sitemesh.xml文件裏,每一個映射器都是它上一個映射器的父映射。當SiteMesh需要一個修飾器來修飾頁面的時候,會在sitemesh.xml裏查找映射器,生成找到的第一個映射器的實例並調用getDecorator()方法,在這個方法裏嘗試查找針對那個頁面的修飾器。如果找到了就返回;否則,調用父映射器的getDecorator()方法,反覆進行這個過程,直到找到正確的修飾器。

b.應用修飾

找到修飾器後,SiteMesh會把請求分發給它。修飾器JSP頁面會訪問在前階段裏解析出來的頁面信息。使用各種SiteMesh自定義標籤來提取頁面信息不同的部分(比如header、footer和title)並把它們插入到輸出文件合適的位置上去。

你可以在sitemesh.xml文件裏自定義使用哪個頁面解析器來解析指定的內容類型或者使用哪種修飾器映射方案,比如:

<?xml version="1.0" encoding="UTF-8"?>
<sitemesh>
<property name="decorators-file" value="/WEB-INF/decorators.xml"/>
<excludes file="${decorators-file}"/>
<page-parsers>
<parser content-type="text/html"
class="com.opensymphony.module.sitemesh.parser.FastPageParser" />
</page-parsers>
<decorator-mappers>
<mapper
class="com.opensymphony.module.sitemesh.mapper.PageDecoratorMapper">
<param name="property.1" value="meta.decorator" />
<param name="property.2" value="decorator" />
</mapper>
<!-- Mapper for localization -->
<mapper
class="com.opensymphony.module.sitemesh.mapper.LanguageDecoratorMapper">
<param name="match.en" value="en" />
<param name="match.zh" value="zh" />
</mapper>
<!-- Mapper for browser compatibility -->
<mapper
class="com.opensymphony.module.sitemesh.mapper.AgentDecoratorMapper">
<param name="match.MSIE" value="ie" />
<param name="match.Mozilla/" value="ns" />
</mapper>
<mapper
class="com.opensymphony.module.sitemesh.mapper.PrintableDecoratorMapper">
<param name="decorator" value="printable" />
<param name="parameter.name" value="printable" />
<param name="parameter.value" value="true" />
</mapper>
<mapper
class="com.opensymphony.module.sitemesh.mapper.ParameterDecoratorMapper">
<param name="decorator.parameter" value="decorator" />
<param name="parameter.name" value="confirm" />
<param name="parameter.value" value="true" />
</mapper>
<mapper
class="com.opensymphony.module.sitemesh.mapper.ConfigDecoratorMapper">
<param name="config" value="${decorators-file}" />
</mapper>
</decorator-mappers>
</sitemesh>

在這個列表裏,<property name="decorators-file">指定了用於定義修飾器的文件。<page-parsers>定義了SiteMesh可以處理的內容類型。每一個<parser>子元素指定哪一個解析器解析哪一種特定的內容類型。在我們的示例sitemesh.xml文件裏,我們告訴SiteMesh使用FastPageParser解析text/html類型的內容。默認地,SiteMesh只可以處理HTML,但我們可以創建自己的解析器來處理其他的內容類型。

<decorator-mappers>子元素定義了映射方案,SiteMesh使用這個映射方案來查找修飾指定頁面的修飾器。你可以使用<param>子元素來配置每一個映射器。SiteMesh會把這些配置信息包裝成java.util.Properties對象傳遞給映射器的init()方法。

區域相關的修飾器

在我們的示例sitemesh.xml文件裏,有下面幾行標籤:

<mapper class="com.opensymphony.module.sitemesh.mapper.LanguageDecoratorMapper">
<param name="match.en" value="en" />
<param name="match.zh" value="zh" />
</mapper>

當查找一個應用於頁面的修飾器時,SiteMesh會首先讀取請求頭部的Accept-Language信息。如果匹配en區域,SiteMesh會在修飾器JSP文件名末尾追加-en。在我們的例子裏,如果請求定義了修飾器headerfooter.jsp的help.jsp頁面,並且使用的是區域是英國,SiteMesh會首先查找並應用headerfooter-en.jsp修飾器,如果找不到再去應用headerfooter.jsp。

瀏覽器相關的修飾器

可以使用AgentDecoratorMapper來保證瀏覽器的兼容性:

<mapper
class="com.opensymphony.module.sitemesh.mapper.AgentDecoratorMapper">
<param name="match.MSIE" value="ie" />
<param name="match.Mozilla/" value="ns" />
</mapper>

這意味着當SiteMesh查找一個修飾器來修飾頁面的時候,會首先提取出請求頭部的User-Agent信息。如果是IE,就加上-ie到修飾器的文件名末尾,並查找和應用這個修飾器。如果找不到這樣的修飾器,則繼續應用headerfooter.jsp。

高級SiteMesh

SiteMesh提供映射器,讓每一個頁面參與到尋找自己修飾器的過程中去。

PrintableDecoratorMapper

大多數的web站點都提供了一個獲得可打印版本頁面的功能。所謂可打印版本,一般是指去除了頁頭、頁尾和頁邊菜單,並使用了另一套樣式表的頁面。在SiteMesh裏,我們可以使用PrintableDecoratorMapper來提供這個功能。要使用這個映射器,需要在sitemesh.xml裏追加如下幾行:

<mapper
class= "com.opensymphony.module.sitemesh.mapper.PrintableDecoratorMapper">
<param name="decorator" value="printable" />
<param name="parameter.name" value="printable" />
<param name="parameter.value" value="true" />
</mapper>

傳遞給PrintableDecoratorMapper的三個配置參數會被包裝成java.util.Properties對象傳遞給init()方法。

•decorator
用來生成可打印版本頁面的修飾器名。

•parameter.name
用來通知SiteMesh我們需要一個可打印版本的請求參數名。比如在我們的例子裏,通過在查詢字符串裏追加printable=true參數傳遞

•parameter.value
設置可打印參數爲何值時SiteMesh提供可打印版本的頁面。

PageDecoratorMapper

頁面可以通過定義META屬性來重載指定修飾自己的修飾器名。

要使用這個映射器,需要在sitemesh.xml文件里加入如下幾行:

<mapper
class="com.opensymphony.module.sitemesh.mapper.PageDecoratorMapper">
<param name="property.1" value="meta.decorator" />
</mapper>

PageDecoratorMapper可以獲取一個參數列表。在我們的例子裏,提供了一個參數名,指定了通過META屬性來取得修飾器名。所以如果我們希望使用test修飾器來修飾頁面,則在該頁頭部加入:

<META name="decorator" content="test">

PageDecoratorMapper提供了一種靜態的方法來讓頁面選擇自己想要使用的修飾器。另外,頁面還可以通過使用ParameterDecoratorMapper在運行時指定要使用的修飾器。

ParameterDecoratorMapper

要使用ParameterDecoratorMapper,在sitemesh.xml裏追加如下幾行:

<mapper
class= "com.opensymphony.module.sitemesh.mapper.ParameterDecoratorMapper">
<param name="decorator.parameter" value="decorator" />
<param name="parameter.name" value="confirm" />
<param name="parameter.value" value="true" />
</mapper>

三個參數的意義分別如下:

•decorator.parameter
指定修飾器所使用的請求參數名。

•parameter.name
確定使用請求修飾器的確認參數名。

•parameter.value
確定使用請求修飾器的確認參數值。

比如,如果你想使用test修飾器來修飾help.jsp,可以像下面這樣訪問help.jsp

help.jsp?decorator=test&confirm=true

除了以上這些映射器以外,SiteMesh還提供了更多有用的映射器,比如:

•FrameSetDecoratorMapper
當頁面是Frame的時候使用。

•CookieDecoratorMapper
可以通過cookie來指定想要使用的修飾器。

•RobotDecoratorMapper
當請求者被確人爲robot的時候使用指定的修飾器。你可以手動的在請求頭部追加robot關鍵字,或者通過修飾器來做。

Velocity 和 Freemarker 修飾器

SiteMesh並沒有限制你只能修飾JSP頁面。你可以自由的選擇想要修飾的對象,比如Velocity或者Freemarker。Velocity和Freemarker是一種可被用於生成web頁面的模板語言。這些語言比JSP更加的簡單易用,但在可編程性方面不如JSP靈活。

SiteMesh通過兩個servlet支持這兩種模板語言,這兩個servlet也被定義在SiteMesh.jar文件裏。我們可以像這樣在web.xml裏聲明這兩個servlet:

<servlet>
<servlet-name>sitemesh-velocity</servlet-name>
<servlet-class>
com.opensymphony.module.sitemesh.velocity.VelocityDecoratorServlet
</servlet-class>
</servlet>
<!--Declare servlet for handling freemarker requests -->
<servlet>
<servlet-name>sitemesh-freemarker</servlet-name>
<servlet-class>
com.opensymphony.module.sitemesh.freemarker.FreemarkerDecoratorServlet
</servlet-class>
<init-param>
<param-name>TemplatePath</param-name>
<param-value>/</param-value>
</init-param>
<init-param>
<param-name>default_encoding</param-name>
<param-value>ISO-8859-1</param-value>
</init-param>
</servlet>
<!-- Velocity servlet should serve all requests with .vm extension-->
<servlet-mapping>
<servlet-name>sitemesh-velocity</servlet-name>
<url-pattern>*.vm</url-pattern>
</servlet-mapping>
<!-- FreeMarker servlet should serve all requests with .dec extension-->
<servlet-mapping>
<servlet-name>sitemesh-freemarker</servlet-name>
<url-pattern>*.dec</url-pattern>
</servlet-mapping>

當然,我們還需要在lib文件夾裏引入freemarker.jar、velocity-dep.jar和velocity-tools-view.jar。這些jar文件已經包含在SiteMesh的發佈包裏了。下面讓我們修改第一個示例應用,使用Velocity和Freemarker修飾器來取代JSP。在我們第一個示例應用裏定義了兩個修飾器:headerfooter和sidemenu。下面我們創建一個headerfooter.dec:

<html>
<head>
<title>My Site - $Advanced SiteMesh</title>
${head}
</head>
<body>
<table border="1">
<tr>
<td>SiteMesh Corporation</td>
</tr>
<tr>
<td>${body}</td>
</tr>
<tr>
<td>SiteMesh copyright</td>
</tr>
</table>
</body>
</html>

在這個頁面裏,我們使用Freemarker模板來請求header、footer和title,而不是使用JSP自定義標籤,但頁面佈局是一樣的。當容器接收到一個.dec擴展名的頁面請求時,會把這個請求傳遞給FreemarkerDecoratorServlet,後者將會調用FreemarkerDecorator修飾生成的HTML頁面。我們使用$Advanced SiteMesh模板來訪問應用生成的web頁面的title,${head}訪問head,${body}訪問body。Freemarker提供了非常豐富的模板,想深入研究的話可以參考http://www.javaworld.com/jw-01-2001/jw-0119-freemarker.html。

相似的,在decorators目錄下創建sidemenu.vm文件,這是Velocity修飾器文件:

<html>
<head>
<title>My Site - $title</title>
$head
</head>
<body>
<table border="1">
<tr>
<td> SiteMesh Header </td>
</tr>
<tr>
<td> Sidemenu </td>
<td> $body </td>
</tr>
<tr>
<td> SiteMesh Footer </td>
</tr>
</table>
</body>
</html>


使用$title模板取代<decorator:title/>,使用$head和$body Velocity模板來取代相應的JSP自定義標籤。

結論

基於過濾器的SiteMesh是一個非常靈活和簡單易用的修飾器框架。但它還是存在着一些問題。首先,從Servlet 2.3版本纔開始支持過濾器,所以一些早期版本的應用服務器無法支持SiteMesh。在使用SiteMesh之前請先檢查一下您想使用的應用服務器是否支持過濾器。

另外,過濾器只有在使用瀏覽器請求一個頁面的時候才能生效。所以,如果你通過瀏覽器訪問home.jsp,它將被修飾,但如果你使用Servlet的RequestDispatcher.include()或者forward()來控制home.jsp,修飾器就不起作用了。但是不用擔心,從Servlet 2.4版本開始,你可以配置過濾器適用的環境,包括forward和include的情況下都可以使用了。 

中文出處:http://www.cjsdn.net/post/view?bid=29&id=178862

英文出處: http://www.onjava.com/lpt/a/5211


 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章