1.介紹
使用過python django模板的應(yīng)該清楚,django里面有一個(gè)激動(dòng)人心的功能就是模板可以使用類的繼承關(guān)系。 即模板是可以繼承的,并且不限繼承的層次。
如
1. child.html extends base.html
2. grandchild.html extends child.html
這樣,我們?cè)赽ase.html中定義好html布局,然后在“子類”(我們這樣稱呼吧)重定義需要父頁(yè)面內(nèi)容即可
2.jsp繼承示例
現(xiàn)在我們來看jsp中如何實(shí)現(xiàn)此功能(django與下面的聲明類似)。
父頁(yè)面: base.jsp,定義布局
- <%@ taglib uri="http://www.rapid-framework.org.cn/rapid" prefix="rapid" %>
- <html>
- <rapid:block name="head">base_head_content</rapid:block>
- <body>
- <br />
- <rapid:block name="content">base_body_content</rapid:block>
- </body>
- </html>
子頁(yè)面: child.jsp
- <%@ taglib uri="http://www.rapid-framework.org.cn/rapid" prefix="rapid" %>
- <% //重定義父頁(yè)面的內(nèi)容為content的內(nèi)容 %>
- <rapid:override name="content">
- child_body_content
- </rapid:override>
-
- <!-- extends from base.jsp -->
- <%@ include file="base.jsp" %>
如上: 我們使用了兩個(gè)jsp tag: block,override,還有<@include >指令.
下面描述各個(gè)功能:
- override tag: 會(huì)將標(biāo)簽里面的內(nèi)容保存為pageContext的變量,變量名稱為name的屬性加前綴,如 __override__$name = tag內(nèi)容
- block tag: 根據(jù)name屬性名稱,如果發(fā)現(xiàn)__override__$name變量,則顯示被重定義的內(nèi)容,否則顯示自身tag的內(nèi)容.
- <%@include >指令: 通過該指令來實(shí)現(xiàn)繼承(extends)的功能(只能使用這個(gè)技巧),該指令必須放置在頁(yè)面的最后面
輸出:
直接訪問base.jsp輸出:
- <html>
- base_head_content
- <body>
- <br />
- base_body_content
- </body>
- </html>
訪問child.jsp輸出:
- <html>
- base_head_content
- <body>
- <br />
- child_body_content
- </body>
- </html>
可以看到,child.jsp的的body輸出內(nèi)容被重定義了.
現(xiàn)該tag可以實(shí)現(xiàn)jsp的無限級(jí)的繼承關(guān)系. 即 grandchild.jsp 繼承 child.jsp , child.jsp繼承 base.jsp
3.具體實(shí)現(xiàn)方式:
Block Tag:
- public class BlockTag extends TagSupport{
-
- private String name;
-
- public void setName(String name) {
- this.name = name;
- }
-
- @Override
- public int doStartTag() throws JspException {
- return getOverriedContent() == null ? EVAL_BODY_INCLUDE : SKIP_BODY;
- }
-
- @Override
- public int doEndTag() throws JspException {
- String overriedContent = getOverriedContent();
- if(overriedContent == null) {
- return EVAL_PAGE;
- }
-
- try {
- pageContext.getOut().write(overriedContent);
- } catch (IOException e) {
- throw new JspException("tag output error",e);
- }
- return EVAL_PAGE;
- }
-
- private String getOverriedContent() {
- String varName = Utils.getOverrideVariableName(name);
- return (String)pageContext.getAttribute(varName);
- }
- }
Override Tag:
- public class OverrideTag extends BodyTagSupport{
-
- private String name;
-
- public void setName(String name) {
- this.name = name;
- }
-
- @Override
- public int doStartTag() throws JspException {
- return isOverrided() ? SKIP_BODY : EVAL_BODY_BUFFERED;
- }
-
- @Override
- public int doEndTag() throws JspException {
- if(isOverrided()) {
- return EVAL_PAGE;
- }
- BodyContent b = getBodyContent();
- String varName = Utils.getOverrideVariableName(name);
- pageContext.setAttribute(varName, b.getString());
- return EVAL_PAGE;
- }
-
- private boolean isOverrided() {
- String varName = Utils.getOverrideVariableName(name);
- return pageContext.getAttribute(varName) != null;
- }
-
- }
工具類:
- class Utils {
- public static String BLOCK = "__override__";
- public static String getOverrideVariableName(String name) {
- return BLOCK + name;
- }
- }
可以看到,實(shí)現(xiàn)代碼不需要100行,重要是的實(shí)現(xiàn)技巧,由此,你從此可以放棄使用sitemesh. 并且如上實(shí)現(xiàn)方式具有更高性能。
并且筆者也擴(kuò)展了freemarker模板,通過自定義指令,實(shí)現(xiàn)如上相同的功能。
該內(nèi)容以后也會(huì)隨rapid-framework一起發(fā)布