1.序言
假设你打算结合多种技术来构建一个企业级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)
2.安装和设置
按照以往的经验,学习任何新技术或新框架最好的办法,就是使用它来创建一个简单的应用程序。所以,我们将使用SiteMesh来创建一个简单的Struts应用程序。我们的应用程序包括三个页面:
•一个登录页面
•一个帮助页面,包括页头和页脚
•一个主页面,包括页头、页脚和页边菜单
3.下面是创建这个简单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包。
<decorators defaultdir="/decorators">decorators.xml 文件用来在你的应用程序里定义修饰器(decorators)。在这个文件里,每个<decorator>元素定义一个修饰器,name指定 修饰器名,page指定修饰器所使用的JSP页面。<pattern>子元素指定这些修饰器如何应用到实际的页面上去。
<!— 给需要页边菜单的页面配置页边菜单修饰器 -->
<decorator name="sidemenu" page="sidemenu.jsp">
<pattern>/home.jsp</pattern>
</decorator>
<!— 给需要页头和页脚的页面配置页头页脚修饰器 -->
<decorator name="headerfooter" page="headerfooter.jsp">
<pattern>/help.jsp</pattern>
</decorator>
</decorators>
另外,过滤器只有在使用浏览器请求一个页面的时候才能生效。所以,如果你通过浏览器访问home.jsp,它将被修饰,但如
果你使用Servlet的RequestDispatcher.include()或者forward()来控制home.jsp,修饰器就不起作用了。
但是不用担心,从Servlet 2.4版本开始,你可以配置过滤器适用的环境,包括forward和include的情况下都可以使用了。
一 个SiteMesh修饰器其实就是一个使用SiteMesh自定义标签的JSP页面。在我们的web应用里,当用户请求help页面的时 候,SiteMesh会拦截这个请求,然后再把它发送给web应用。而当应用返回响应的时候,SiteMesh会结合headerfooter.jsp文 件解析这个响应,遇到<decorator:head/>就插入响应文件的<head>,遇 到<decorator:body/>就插入响应文件的<body>。最后,被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>
<HTML>这是一个在web应用里很常见的help页面。
<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>
<?xml version="1.0" encoding="UTF-8"?>在 这个列表里,<property name="decorators-file">指定了用于定义修饰器的文件。<page-parsers>定义了SiteMesh可 以处理的内容类型。每一个<parser>子元素指定哪一个解析器解析哪一种特定的内容类型。在我们的示例sitemesh.xml文件里, 我们告诉SiteMesh使用FastPageParser解析text/html类型的内容。默认地,SiteMesh只可以处理HTML,但我们可以 创建自己的解析器来处理其他的内容类型。
<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>
<mapper class="com.opensymphony.module.sitemesh.mapper.LanguageDecoratorMapper">当 查找一个应用于页面的修饰器时,SiteMesh会首先读取请求头部的Accept-Language信息。如果匹配en区域,SiteMesh会在修饰 器JSP文件名末尾追加-en。在我们的例子里,如果请求定义了修饰器headerfooter.jsp的help.jsp页面,并且使用的是区域是英 国,SiteMesh会首先查找并应用headerfooter-en.jsp修饰器,如果找不到再去应用headerfooter.jsp。
<param name="match.en" value="en" />
<param name="match.zh" value="zh" />
</mapper>
<mapper这意味着当SiteMesh查找一个修饰器来修饰页面的时候,会首先提取出请求头部的User-Agent信息。如果是IE,就加上-ie到修饰器的文件名末尾,并查找和应用这个修饰器。如果找不到这样的修饰器,则继续应用headerfooter.jsp。
class="com.opensymphony.module.sitemesh.mapper.AgentDecoratorMapper">
<param name="match.MSIE" value="ie" />
<param name="match.Mozilla/" value="ns" />
</mapper>
<mapper传递给PrintableDecoratorMapper的三个配置参数会被包装成java.util.Properties对象传递给init()方法。
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>
class="com.opensymphony.module.sitemesh.mapper.PageDecoratorMapper">PageDecoratorMapper可以获取一个参数列表。在我们的例子里,提供了一个参数名,指定了通过META属性来取得修饰器名。所以如果我们希望使用test修饰器来修饰页面,则在该页头部加入:
<param name="property.1" value="meta.decorator" />
</mapper>
<filter-mapping>
<filter-name>sitemesh</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<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>
<servlet>当 然,我们还需要在lib文件夹里引入freemarker.jar、velocity-dep.jar和velocity-tools- view.jar。这些jar文件已经包含在SiteMesh的发布包里了。下面让我们修改第一个示例应用,使用Velocity和Freemarker 修饰器来取代JSP。在我们第一个示例应用里定义了两个修饰器:headerfooter和sidemenu。下面我们创建一个 headerfooter.dec:
<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>
<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。
<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>
<html>使用$title模板取代<decorator:title/>,使用$head和$body Velocity模板来取代相应的JSP自定义标签。
<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>