引入:
最近在review别的团队的代码,看到他们团队使用了sitemesh,以前对这个不是很了解,刚好借助他们源代码,这里简单研究了下:
简单介绍:
其实sitemesh使用了页面装饰技术,它主要就是用一个或者多个装饰器应用于某些给定页面,最终产生被装饰器装饰后的页面。
具体过程是:
(1)sitemesh通过Filter来拦截页面访问 (可以猜想:肯定会有个url-pattern,表明此url被适用于某装饰器,等会具体讲 )
(2)一旦拦截到,则根据被访问的url来找到合适的装饰模板(可以猜想:肯定有一个模板定义文件,在其中定义了模板和模板使用的url之间的映射关系)
(3)提取被访问的页面的内容,放到装饰模板中给定的位置(这几乎是废话,因为这是组合页面技术,肯定输入有2个,一个是模板,一个是被访问页面,模板肯定会给出一些可配置的点,然后用placeholder表示,然后这些placeholder中放入的内容肯定由真实页面提供,放真实页面的内容肯定有个标记,这个标记符合模板上的标记从而不会找错,等会可以验证)
(4)把装饰后的页面发送给客户端。
以上是我对于这个框架的开始的猜想,接下来就是去验证这些猜想。
实践:
(1)首先,如何做到sitemesh通过filter来拦截页面访问呢?根据常识,一般web应用过滤器都是定义在web.xml中的,所以我们去找:
<!-- sitemesh config --> <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>
从这里可以很清楚的看到,我们配置了 sitemesh过滤器,然后要对所有的页面都适用这个过滤器,当然了,要过滤器生效,还必须有相应的jar包,并且放在WEB-INF/lib下,这个是常识,因为他们项目中使用了maven,所以我们肯定会在pom.xml中找到对应的depenency:
<!-- sitemesh --> <dependency> <groupId>opensymphony</groupId> <artifactId>sitemesh</artifactId> <version>2.4.2</version> </dependency>
(2)现在我们来看第二步,肯定有个模板定义文件,定义了模板文件和其适用的文件之间的关系。这里就有2个概念了,一个是一般文件(可以说比较素的文件), 一种是模板文件(它定义了比较花哨的结构和外观),素文件我们不讲了,就是一般页面(比如jsp),然后模板文件,肯定是带有placeholder的页面文件,所以我们找到了这些文件:
显然,在他们的项目中,这些模板文件是放在WEB-INF/decorators中的,并且从这里可以看出他们定义了2个模板文件,一个叫blog_main.jsp,另一个叫main.jsp,然后他们的一般文件都是放在WEB-INF/jsp中的来加强安全性。我们随便打开一个模板文件,比如blog_main.jsp:
<!DOCTYPE HTML> <%@ taglib uri="http://www.opensymphony.com/sitemesh/decorator" prefix="decorator"%> <%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%> <html lang="en"> <head> <link rel="icon" href="${contextPath}/p_w_picpaths/favicon.ico" type="p_w_picpath/x-icon" /> <link rel="shortcut icon" href="${contextPath}/p_w_picpaths/favicon.ico" type="p_w_picpath/x-icon" /> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link rel="stylesheet" href="${cssRootPath}/common.css"> <jsp:include page="${jspRootPath}/includes/global/incScript.jsp"/> <!-- 从被装饰页面获取title标签内容,并设置默认值--> <title><decorator:title default="Blog - Shokay"/></title> <!-- 从被装饰页面获取head标签内容 --> <decorator:head/> </head> <body> <jsp:include page="${jspRootPath}/includes/common/incPageTopHideBar.jsp" /> <jsp:include page="${jspRootPath}/includes/common/incPageTopBar_blog.jsp" /> <div class="main-body"> <decorator:body /> </div> </body> <jsp:include page="${jspRootPath}/includes/common/incPageFooterBlog.jsp" /> </html>
果然和我们猜想一样,这个页面是一个有占位符的文件,所以是模板文件,并且模板文件用的标签前缀都是 <decorator>,所以这个模板文件中有3个可插入内容的点,一个是<decorator:title/>,一个是<decorator:head/>,一个是<decorator:body/>, 并且查询相关文章可以知道,整合素文件的过程是从素文件中分别提取出<title>,<head><body> 元素并且插入到相应的位置,如果没有相应的内容则使用后面的default默认值。当然了,<decorator>标记还有其他的内容,我们这里不一一阐述。
当然了,国际化惯例,为了使用这个标记,在页首必须声明标记前缀。从这里可以看出来,这个前缀来自http://www.opensymphony.com/sitemesh/decorator这个标记库
然后我们去探讨模板和适用页面之间的映射关系了,常识告诉我们肯定是在xml文件中配置,我们很快找到了这个映射文件,WEB-INF/decorators.xml
<?xml version="1.0" encoding="ISO-8859-1"?> <decorators defaultdir="/WEB-INF/decorators"> <!-- Any urls that are excluded will never be decorated by Sitemesh --> <excludes> <pattern>/changepassword*</pattern> <pattern>/*Ajax</pattern> <pattern>/*ajax</pattern> <pattern>/p_w_picpath</pattern> <pattern>/checkout/*</pattern> <pattern>/admin/search/*</pattern> <pattern>/index</pattern> </excludes> <!-- blog --> <decorator name="blog" page="blog_main.jsp"> <pattern>/blog/*</pattern> </decorator> <!-- main --> <decorator name="main" page="main.jsp"> <pattern>/*</pattern> </decorator> <decorator name="none"> <pattern>/index.jsp</pattern> </decorator> </decorators>
果然和设想一样,我们在WEB-INF/decorators中定义的2个模板文件在这里都有定义,并且每个模板文件(对应某个装饰器)都有给定的<pattern>,表明此装饰器使用的URL集合,我们找到了刚才的blog_main.jsp这个装饰页面使用的集合是/blog/ 开头的任意url。
当然了, 这里还可以使用<exclude>来控制那些页面不受到装饰模板的控制。
也许你会问,我们这个定义文件是否一定要叫WEB-INF/decorators.xml呢?其实不一定的,默认是在WEB-INF下的decorators.xml,(约定优于配置) ,这个约定在sitemesh.jar中。如果你要显式定制它的位置的话,那么它在sitemesh.xml中配置了位置:
<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.HTMLPageParser" /> </page-parsers> <decorator-mappers> ... </sitemesh>
从上可以看出,用于装饰模板的文件是定义在/WEB-INF/decorators.xml中。