最近在一个项目中使用 SiteMesh ,感觉还不错,所以拿出来总结总结。首先,通过网上资料发现 SiteMesh 是由一个基于 Web 页面布局、装饰以及现存 Web 应用整合的框架。它能帮助我们在由大量页面构成的项目中创建一致的页面布局和外观,如一致的导航条、一致的菜单、一致的版权信息等等。它不仅仅能处理动态的内容,如 JSP 、 ASP 、 PHP 等产生的内容,也能处理静态的内容,如 HTML 的内容,使得它的内容也符合我们自己的页面结构的要求。甚至于它能将 HTML 对象像 include 那样将该文件作为一个面板的形式嵌入到别的文件中去。所有的这些都是 GOF 的 Decorator 模式可以实现的。
SiteMesh 框架是 OpenSymphony 团队开发的一个非常优秀的页面装饰器框架,它通过对用户请求进行过滤,并对服务器向客户端响应也过滤(过滤之后加载需要的菜单、导航以及版权信息),然后给原始页面加入一定的装饰( header 、 footer 等),然后把结果返回给客户端。通过 SiteMesh 的页面装饰,可以提供更好的代码复用,所有的页面装饰效果耦合在目标页面中,无需再使用 include 指令来包含装饰效果,目标页与装饰页完全分离(这样不同的人只需要主要自己的子页面,最后由管理人员统一进行请求地址的配置),如果所有页面使用相同的装饰器,可以使整个 Web 应用具有统一的风格,官网: http://www.opensymphony.com/sitemesh/ 。
上面的过滤还想多细说一下,就是说用户的一个请求到服务器之后,如果该请求需要 SiteMesh 装饰,服务器先解释被请求的资源,然后根据配置文件获取用于该请求的装饰器,最后用装饰器装饰被请求资源,将结果一同返回给客户端浏览器。(这里需要注意的是,有时候我们前台是用过 Ajax 请求到后台服务器的,而服务器返回一个 JSON 对象,这时我们就需要指定 SiteMesh 不要对这个 JSON 对象进行装饰,否则,前台 JS 是无法将装饰后的 JSON 对象转化为 JSON 的。 )
废话说了一大堆,下面我们来看看使用 SiteMesh 的流程:
1、 导入相关 jar 包 sitemesh-2.4.jar
2、 修改 web.xml ,在里面加入 SiteMesh 的过滤器
<filter> <filter-name>sitemesh</filter-name> <filter-class> com.opensymphony.module.sitemesh.filter.PageFilter </filter-class> </filter> <filter-mapping> <filter-name>sitemesh</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
这里网上说位置:应该在 struts2 的 org.apache.struts2.dispatcher.ActionContextCleanUp 过滤 器之后 ,org.apache.struts2.dispatcher.FilterDispatcher 过滤器之前,否则会有问题。
3、 在下载的 SiteMesh 包中找到 sitemesh.xml , (\sitemesh\src\example-webapp\WEB-INF 目录下 ) 将其拷贝到 /WEB-INF 目录下;
4、 在 WEB-INF 下新建 decorators.xml 文件
<?xml version="1.0" encoding="UTF-8"?> <decorators defaultdir="/decorators"> <!-- 此处用来定义不需要过滤的页面 --> <excludes> <!--一般比如Ajax请求的地址都不需要过滤 --> <pattern>/fdvequipmentcontrol/getFdvEquipmentsByAreaId.ac*</pattern> </excludes> <!-- 用来定义装饰器要过滤的页面,这里的page可以直接是一个JSP页面,也可以是一个后台请求地址 --> <decorator name="inpTemplate" page="/login/inpTemplate.ac"> <pattern>/intgMainIndex/*</pattern> <pattern>/login/toIntegrateIndex.ac</pattern> </decorator> </decorators>
decorators 结点的 defaultdir 属性指定了模板文件存放的目录 ;
5、 添加模版页
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@taglib prefix="decorator" uri="http://www.opensymphony.com/sitemesh/decorator"%>
<%@taglib prefix="page" uri="http://www.opensymphony.com/sitemesh/page"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title><decorator:title default="Welcome to test sitemesh!" /></title>
<!-- 页面Head由引用模板的子页面来替换-->
<decorator:head />
</head>
<body>
<div class="menu">
…
</div>
<div class="container">
<!-- 这里的内容由引用模板的子页面来替换 -->
<decorator:body />
</div>
</div>
<div class="foot">XXXXX ©2012</div>
</body>
</html>
这就是简单的模板,页面的头、菜单、导航和脚等都是由模板里的静态 HTML 决定,主页面区域的是 <decorator:body /> 标签,也就是说凡是能进入过滤器的请求生成的页面都会默认加上模板上的头、菜单、导航和脚等公用信息,然后将页面自身的内容自动放到 <decorator:body /> 标签所在的位置。 <decorator:title default="Welcome to test sitemesh!" /> ,读取被装饰页面的标题,并给出了默认标题。 <decorator:head /> :读取被装饰页面的 <head> 中的内容;
最后说说在项目中使用的体会,使用 SiteMesh 个人认为它不仅仅统一了页面的风格和布局,同时让团队中的各个开发人员更加注重我们自己的子模块的开发,或者说更加关注我们的业务逻辑,而不用每个开发人员都去大量用 include 方式复用页面。它提供了很大的灵活性以及给我们提供了整合异构 Web 系统页面的一个方案。不过在使用时,倒是遇到一个问题 SiteMesh 貌似没什么好的方法,就是项目中使用 SpringMVC 时,比如进入编辑和保存编辑请求的 URL 是同一个 URL ,但请求的方式一个是 GET 一个是 POST 的话, SiteMesh 就没法对这两个请求一个过滤一个不过滤了,后来自己将这两个相同的 URL 改成不同的了。