使用 SiteMesh3 完善页面布局

在网页开发中, 大部分网页都具有相同的页头, 页尾, 菜单等模块. 一般情况下我们会将这些共用的代码单独抽取成一个页面, 然后进行包含. 虽然这样能够达到代码复用的效果, 但是如果引入的页面过多, 一来会带来修改不变的效果, 二来依然会形成多个页面使用相同的代码 (页面包含代码), 此时我们可以使用 SiteMesh3 来妥善解决这个问题

最初页面形式

最初页面形式

使用 JSP 进行页面包含

页面包含

进行页面包含后, 能达到一些共用页面的代码复用, 但是如果页面布局复杂的话, 会存在大量的页面包含代码, 依然给我们带来了不便

SiteMesh3 方式

SiteMesh3 使用演示

使用 SiteMesh3 时需要先定义一个装饰器, 在这个装饰器中我们可以定义页面的布局, 然后配置动态内容输出位置即可
示例如下

<!DOCTYPE>
<html lang="zh-cmn-Hans">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    
    <!--<sitemesh:write property="title"/> 会输出原始页面的 title 标签里面的内容 -->
    <title><sitemesh:write property="title"/></title>
    
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
    <meta name="renderer" content="webkit"/>
    <link rel="stylesheet" type="text/css" href="/static/css/web.css"/>
    
    <!--<sitemesh:write property="head"/> 会输出原始页面 head 标签里面的内容 (不包括 title 标签)-->
    <sitemesh:write property="head"/>
</head>
<body>
    <header>header</header>
    
    <!--<sitemesh:write property="body"/> 会输出原始页面 body 标签里面的内容 -->
    <sitemesh:write property="body"/>
    
    <footer>footer</footer>
    <script type="text/javascript" src="/static/js/web.js"></script>
</body>
</html>

假如此时存在一个这样的原始页面

<!DOCTYPE>
<html>
<head>
    <title>title</title>
    <meta name="keywords" content="SiteMesh3">
</head>
<body>
        hello,world
</body>
</html>

经过 SiteMesh3 装饰后的页面如下

<!DOCTYPE>
<html lang="zh-cmn-Hans">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    
    <title>title</title>
    
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
    <meta name="renderer" content="webkit"/>
    <link rel="stylesheet" type="text/css" href="/static/css/web.css"/>
    
    <meta name="keywords" content="SiteMesh3">
    
</head>
<body>

    <header>header</header>
        hello,world
    <footer>footer</footer>
    <script type="text/javascript" src="/static/js/web.js"></script>
</body>
</html>

可以发现使用 SiteMesh3 进行装饰能够让我们更加专注于一些与页面独有的代码逻辑, 能避免相同的代码在多个页面重复出现

SiteMesh3 使用说明

通过上面的一个小例子, 我们可以发现使用 SiteMesh3 需要一个装饰器页面. 由此可以牵扯出另外几个问题, 对哪些页面进行装饰? 使用哪个装饰页面装饰?
通过以下配置可以完成这些疑问
首先我们需要引入 SiteMesh3 相关的 jar 包

<dependency>
    <groupId>org.sitemesh</groupId>
    <artifactId>sitemesh</artifactId>
    <version>3.0.1</version>
</dependency>

其次, SiteMesh3 会对一些页面进行装饰, 所以我们需要添加一个过滤器来进行页面过滤

<filter>
    <filter-name>sitemesh</filter-name>
    <filter-class>org.sitemesh.config.ConfigurableSiteMeshFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>sitemesh</filter-name>
    <!-- 如果项目中的页面地址以  .do 或者 .action 结尾可以使用 *.do 或 *.action-->
    <url-pattern>/*</url-pattern>
</filter-mapping>

同时我们还需要配置装饰器, 要装饰的页面, 不需要装饰的页面等信息
WEB-INF 目录下创建一个 sitemesh3.xml 文件,请确保路径正确,SiteMesh3会根据 /WEB-INF/sitemesh3.xml 加载文件
文件内容如下

<?xml version="1.0" encoding="UTF-8"?>
<sitemesh>
    <!-- path 是指要进行装饰的页面 如 /index.jsp 对应 http://localhost:8080/index.jsp -->
    <!-- decorator 是指装饰器页面 /decorators/default.jsp 对应位置为 src/main/webapp/decorators/default.jsp--> 
    <!-- sitemesh3 会在 index.jsp 页面返回时提取 title head body 中的内容, 然后在 default.jsp 根据输出标签进行对应输出 -->
    <mapping path="/index.jsp" decorator="/decorators/default.jsp"/>
</sitemesh>

此时我们可以将上面演示中的文件内容建立 index.jspdefault.jsp 页面, 然后进行测试访问, 会发现能达到演示中的效果

SiteMesh3 高级配置

通过上面的简单配置我们可以实现一个最基本的页面装饰, 与此同时 SiteMesh3 还支持一些更为高级的配置

  • 默认装饰器
<!-- 如果不填写 path 路径, 则 SiteMesh3 会在找不到匹配的装饰器时, 使用这个装饰器进行装饰 -->
<!-- 推荐在该页面中定义一些全网站共用的代码, 如: 网站统计相关脚本, 浏览器渲染方式等 meta 标签 -->
<mapping decorator="/decorators/default.jsp"/>
  • 多个装饰器
<mapping>
    <path>/multi.jsp</path>
    <decorator>/decorators/multi_1.jsp</decorator>
    <decorator>/decorators/multi_2.jsp</decorator>
    <decorator>/decorators/multi_3.jsp</decorator>
</mapping>

装饰器会按照配置的先后顺序进行装饰
假如
multi.jsp 中 body 内容为 0,
multi_1.jsp 中内容为 1 同时在下面输出原始页面中的body内容

<body>
    <h3>1</h3>
    <sitemesh:write property="body"/>
</body>

multi_2.jsp 中内容为 2 同时在下面输出原始页面中的body内容

最终返回的页面内容为
2
1
0

  • 不需要装饰
<!-- SiteMesh3 会先判断是否不需要装饰, 然后再判断是否存在匹配的装饰器 -->
<!-- /exclude/* 会对所有以 /exclude 开头的 url 都不会进行装饰 包括 /exclude/index.jsp,/exclude/item/index.jsp -->
<mapping path="/exclude/*" exclue="true"/>
  • MIME 类型
<!-- 
默认情况下, SiteMesh3 只对响应头 Content-Type 中包含 text/html 的页面进行装配
如果会需要装配其他格式的页面, 添加多个 mime-type 标签即可
-->
<mime-type>text/html</mime-type>
<mime-type>application/vnd.wap.xhtml+xml</mime-type>
<mime-type>application/xhtml+xml</mime-type>
  • 自定义输出标签
<!--
输出标签格式 <sitemesh:write property="body"/>
SiteMesh3 默认支持 title, head, body 三个标签, 如果需要支持其他标签, 可以通过实现 org.sitemesh.content.tagrules.TagRuleBundle 接口中的 install 方法来完成对标签的拓展
-->
<content-processor>
    <tag-rule-bundle class="com.github.ghthou.learning.sitemesh3.ExpandTagRuleBundle"/>
</content-processor>

ExpandTagRuleBundle 代码如下

public class ExpandTagRuleBundle implements TagRuleBundle {

    @Override
    public void install(State defaultState, ContentProperty contentProperty, SiteMeshContext siteMeshContext) {
        defaultState.addRule("header", new ExportTagToContentRule(siteMeshContext, contentProperty.getChild("header"), false));
        defaultState.addRule("menu", new ExportTagToContentRule(siteMeshContext, contentProperty.getChild("menu"), false));
        defaultState.addRule("footer", new ExportTagToContentRule(siteMeshContext, contentProperty.getChild("footer"), false));
    }

    @Override
    public void cleanUp(State defaultState, ContentProperty contentProperty, SiteMeshContext siteMeshContext) {

    }
}

此时可以使用 <sitemesh:write property="header"/> 标签在装饰器中输出原始页面的 header 标签值

  • 其他说明

    • title, head, body 标签的实现请参考 org.sitemesh.content.tagrules.html.CoreHtmlTagRuleBundle
    • title, head, body 等自定义标签只会提取原始页面中的第一个找到的标签
    • 默认配置文件路径为 /WEB-INF/sitemesh3.xml ,如果需要自定义配置路径,请在配置 filter 时配置 configFile 属性,如将 sitemesh3.xml 配置文件放在 resources 文件夹中
    <filter>
        <filter-name>sitemesh</filter-name>
        <filter-class>org.sitemesh.config.ConfigurableSiteMeshFilter</filter-class>
        <!--设置配置文件路径 默认为 /WEB-INF/sitemesh3.xml -->
        <init-param>
            <param-name>configFile</param-name>
            <param-value>/WEB-INF/classes/sitemesh3.xml</param-value>
        </init-param>
    </filter>
    
    • 如果装饰器是 html 文件会存在中文乱码的问题, 在 SpringMVC 中可在 web.xml 文件配置如下过滤器
    <filter>
        <filter-name>CharacterEncodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>utf-8</param-value>
        </init-param>
        <init-param>
            <param-name>forceEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>CharacterEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    
    • 在 SpringMVC 中因为 SiteMesh3 需要直接访问到装饰页面, 所以需要增加如下标签
    <mvc:default-servlet-handler/>
    <!-- 或者配置 url 与文件夹映射关系 -->
    <mvc:resources mapping="/decorators/**" location="/decorators/"/>
    
    • 如果装饰器为 JSP 页面, 则可以使用 EL 表达式获取原始页面中的属性
      假如在 SpringMVC 中设置如下属性
    @RequestMapping(value = "/index")
    public String index(Model model) {
        model.addAttribute("date", new Date().toLocaleString());
        return "index";
    }
    
      则可以在方法返回页面 `/index.jsp` 和该 url 的装饰页面 `/decorators/default.jsp` 中使用 EL 表达式获取 `date` 属性
    

参考资料

SiteMesh3 官网文档
SiteMesh3 GitHub

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容

  • 今天改前人做的项目,用struts2,spring,hibernate框架做的,对了,还有jquery。我用jqu...
    JingBeibei阅读 679评论 0 2
  • Spring Boot 参考指南 介绍 转载自:https://www.gitbook.com/book/qbgb...
    毛宇鹏阅读 46,724评论 6 342
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,562评论 18 399
  • 一. Java基础部分.................................................
    wy_sure阅读 3,785评论 0 11
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,587评论 18 139