引入:
最近在review别的团队的代码,看到他们团队使用了sitemesh,以前对这个不是很了解,刚好借助他们源代码,这里简单研究了下:
简单介绍:
其实sitemesh使用了页面装饰技术,它主要就是用一个或者多个装饰器应用于某些给定页面,最终产生被装饰器装饰后的页面。
具体过程是:
(1)sitemesh通过Filter来拦截页面访问 (可以猜想:肯定会有个url-pattern,表明此url被适用于某装饰器,等会具体讲 )
(2)一旦拦截到,则根据被访问的url来找到合适的装饰模板(可以猜想:肯定有一个模板定义文件,在其中定义了模板和模板使用的url之间的映射关系)
(3)提取被访问的页面的内容,放到装饰模板中给定的位置(这几乎是废话,因为这是组合页面技术,肯定输入有2个,一个是模板,一个是被访问页面,模板肯定会给出一些可配置的点,然后用placeholder表示,然后这些placeholder中放入的内容肯定由真实页面提供,放真实页面的内容肯定有个标记,这个标记符合模板上的标记从而不会找错,等会可以验证)
(4)把装饰后的页面发送给客户端。
以上是我对于这个框架的开始的猜想,接下来就是去验证这些猜想。
实践:
(1)首先,如何做到sitemesh通过filter来拦截页面访问呢?根据常识,一般web应用过滤器都是定义在web.xml中的,所以我们去找:
1
2
3
4
5
6
7
8
9
|
<!-- 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:
1
2
3
4
5
6
|
<!-- 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:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
<!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}/images/favicon.ico"
type
=
"image/x-icon"
/>
<
link
rel
=
"shortcut icon"
href
=
"${contextPath}/images/favicon.ico"
type
=
"image/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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
<?
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
>/image</
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中配置了位置:
1
2
3
4
5
6
7
8
9
|
<
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中。