首先聲明 Tag File 是門老技術,好用之餘知道的人卻不多!
以前我們抽取一段JSP代碼,整合到完整的頁面中,一般使用 include 指令(例如<%@include file="public/nav.jsp"%>),這比較簡單的說。而今天要介紹的是 include 的“進階版”——Tag Files。它着實十分強大,不僅可以完全替代 include,而且還可以建立進階的可複用标簽庫,使得快速開發和維護動态網頁比以前更加容易,甚至網頁作者無須學習 Java 程式語言本身,就能開發出全新的動态網頁。學習過程中,發現以下兩點比較有趣,而且都是往事:
隻恨俺當年有眼不識泰山、相見恨晚呀~呵呵~閑話休提,速速進入 Tag Files 之旅吧!
首先說說怎麼使用 Tag File。拿一個簡單的例子。第一步建立被應用的 HTML 片段,假設目前是 Hello.tag,将它放置在 WEB-INF/tags/ 目錄下。你可以在 Tag File 裡直接使用 JSP 的文法來制作标簽。标簽檔案的擴充名必須是“.tag”。
然後在頁面中使用自定義标簽時,需要先導入标簽庫,再使用标簽。具體在 JSP 網頁使用 Hello.tag 的方法如下:
其中,prefix 用于确定标簽字首;而tagdir标簽庫路徑下存放很多 Tag File,每 Tag File 對應一個标簽。最後執行的結果如下:
十分簡單是吧?其實,再複雜的 Tag Files,也要比原生寫 SimpleTag、寫 Java 代碼來得簡單。所有 JSP 裡面能做的事情,幾乎在 Tag Files 裡面都可以做的,包括模闆語言 JSTL——不過我就沒有推薦使用 JSTL,而是直接 Java if/for 來控制某些頁面邏輯,也就是 <% ...Java code...%>。出于學習成本的原因,我不想再重複學習類似的東西。當然,EL 表達式我是推薦使用的,如果能夠使用 EL 表達式的,盡量使用,能避免 Java Code <%%> 的盡量避免。
該小節小結如下:
導入格式為 <%@taglib prefix="test" tagdir="/WEB-INF/tags"%>
tagdir:用于指定tag檔案目錄,當頁面使用 <ui:xxxx> 會查找該目錄下對應的 xxxx.tag 檔案。
prefix:指定使用時标簽字首
使用:<test:xxxx />
include 指令有個缺點,就是不能對被包含的頁面片段進行參數的傳遞。你可能會想到使用 <jsp:include> 标簽,如:
雖然可以傳參數,但 <jsp:include> 的方式與接着要介紹的 Tag Files 之 attribute 相比,還是弱很多。attribute 支援依賴性是否可選,類型檢查等等更複雜的功能,設定可傳遞一大段 HTML 過去!例如下面一個例子。
本體(本體的描述乃相對于被包含的 Tag File 而言)調用方式:
像“title="首頁"”這樣就完成了 title “參數”的傳遞!當然準确說是 Attribute 屬性。并且聲明該項屬性不能不填(require="true")。而且要求是 String 類型,别的不行哦。還帶有 description 說明呢。另外請注意 type 這裡不支援泛型,是以沒有 Map<String, Object> 那樣的寫法——填 type="Map" 即可;數組也沒問題,填 type="Map[]" 即可。如果前面有 import="java.util.Map" 的話,這裡直接填 type="Map" 即可,無須寫全稱 type="java.util.Map"。
你可能會問,像字元串的類型就可以 title="首頁" 這樣傳,但其他類型呢?你可以直接輸入值,如 array="<%=homeService.getMsg()%>";也可以把其他類型儲存在 request.setAttribute("foo", Object) 中,然後通過 title="${foo}" 傳就可以了。另外對于 Tag File 裡面的值擷取,EL 表達式也是通用的。
tag 指令如同 JSP 網頁的 page 指令,用來設定标簽檔案 <%@tag display-name="" body-content="" dynamic-attributes="" small-icon="" large-icon="" description="" example=""language="" import="" pageEncoding="" isELIgnored="">
body-content 表示可能的值有三種,分别是 empty、scriptless、tagdependent、empty。empty 為标簽中沒有主體内容;scriptlet 為标簽中的主體内容 EL、JSP 動作元素,但不可以為 JSP 腳本元素;tagdependent 表示标簽中的主體内容交由 tag 自己去處理,預設值為 scriptless;
dynamic-attributes 表示設定标簽檔案動态屬性的名稱,當 dynamic- attributes 設定時,将會産生一個 Map 類型的集合對象,用來存放屬性的名稱和值;
description 表示用來說明此标簽檔案的相關資訊;
example 表示用來增加更多的标簽使用說明,包括标簽應用時的範例;
language、import、pageEncoding、 isELIgnored 這些屬性與 page 指令相對應的屬性相同。
支援 HTML 片段傳遞是 Tag Files 的一大特點,簡直令筆者心靈神往。心想,模闆機制能做到這樣,非常不錯!什麼标簽的繼承,都不在話下!
我們把上面的例子改改,變成 Tag:
注意這次 Tag 變成一個完整的 HTML 頁面,而且裡面有個 <jsp:doBody /> 特殊标記哦~。
這樣的話,那麼本體是這樣的:
哈哈~不知你看到沒有,<jsp:doBody /> 所指的就是 <html:Html title=...>(這裡一大段的 HTML) </html:Html> 中間的内容,整段 Form 标簽都傳過去 Tag File 裡面了。
更妙的是,<html:Html title=...>……</html:Html> 裡面還可以是包含有 <%%> 的 incule 指令,注意是包含 <%%>!
如此一來即可規避 body-content="scriptless" 不可出現 <% %>、<%= %> 或 <%! %> 之問題。
更妙的是,兩套模闆之間還可以互相嵌套的,請看:
<jsp:invoke fragment="button"/> 嵌入到 dhtml:msgBox 模闆中去了。
一個 doBoady 是不夠的,——來多幾個怎麼樣?回答是絕對肯定的!隻要我們把 Attibute 聲明為 fragment="true" 即可。例如下面撰寫一個 table.tag:
在這個 Tag File 中,将 attribute 的屬性設定為 Fragment,然後想取得指定的 Fragment 的話,就可以使用 <jsp:invoke> 動作元素,并指定 Fragment 的名稱,使用下面這個 JSP 網頁來測試:
JSP 網頁中,同樣的是使用 <jsp:attribute> 來指定 Fragment 的文字内容,那麼執行這個 JSP 網頁的話會生成以下的内容:
這個指令用來設定自定義标簽的屬性。其中 name 表示屬性的名字:<%@attribute name="" required="" fragment="" rtexprvalue="" type="" description=""%>
required 表示是否為必要,預設為 false;
rtexprvalue 表示屬性值是 否可以為 run-time 表達式。如為 true,表示屬性可用動态的方式來指定,如:<mytag:read num="${param.num}"/>,如為 false,則一定要用靜态的方式來指定屬性值;
type 表示這個屬性的類型,預設值為 java.lang.String;description用來說明此屬性的相關資訊
Tag File 運算過的結果,都可以傳回給本體,灰常強大是吧!?沒錯哦~的确可以。我們通過 <%@variable%> 指令完成。
注意下面的例子, doBody 多了 var="code":
本體檔案:
也許大家會問,這個所謂“強大”的功能有什麼用呢?——很簡單,你想想,把邏輯封裝起來裡,讓變化的隻是标簽,也就是本體裡面的——實際上也是如此,通常變化的都是 HTML 标簽。那麼要輸出的值便是這些變量了,傳回給本體,讓本體去控制顯示。标簽這個特性在制作“标簽疊代器”的時候十分适用,且看例子:
本體:
設定 name-from-attribute 還可以指定傳遞變量的名稱!由主體來定義而不是 Tag File 來定義,也就是說,把上述例子的 current 改為你喜歡的!
這個指令用來設定标簽檔案的變量,其中 name-given 表示直接指定變量的名稱: <%@variable name-given="" name-from-attribute="" alias="" variable-class="" declare="" scope="" desription="">
description 用來說明此變量的相關資訊
name-from-attribute 表示以自定義标簽的某個屬性值 為變量名稱;
alias 表示聲明一個局部範圍屬性,用來接收變量的值;
variable-class 表示變量的類名稱,預設值為 java.lang.String;
declare 表示此變量是否聲明預設值為 true;
scope 表示此變量的範圍,範圍是:AT_BEGIN、 AT_END 和 NESTED,預設值為 NESTED;作用範圍為"NESTED",也就是在起始卷标與結束卷标之間
Tag File 是自定義标簽的簡化。事實上,就如同 JSP 檔案會編譯成 Servlet 一樣, TagFile 也會編譯成 Tag 處理類,自定義标簽的背景依然由标簽處理類完成,而這個過程由容器完成。關于編譯,參見:
前面提過Tag File會被容器轉譯,實際上是轉譯為javax.servlet.jsp.tagext.SimpleTagSupport的子類別。以Tomcat為例,Errors.tag轉譯後的類別原始碼名稱是Errors_tag.java。在Tag File中可以使用out、config、request、response、session、application、jspContext等隱含物件,其中jspContext在轉譯之後,實際上則是javax.servlet.jsp.JspContext物件。 是以,Tag File在JSP中,並不是靜態包含或動態包含,在Tag File中撰寫Scriplet的話,其中的區域變數也不可能與JSP中Scriptlet溝通。 JspContext是PageContext的父類別,JspContext上定義的API不像PageContext有使用到Servlet API,原本在設計上希望JSP的相關實現可以不依賴特定技術(例如Servlet),是以才會有JspContext這個父類別的存在。
附加一個例子:用 TagFile 完成一個疊代器:
參見資源:
《JSP2.0 技術手冊》
<a target="_blank" href="http://book.51cto.com/art/200808/86136.htm">Tag File 支援</a>
<a target="_blank" href="http://openhome.cc/Gossip/ServletJSP/TagFileABC.html">簡介 Tag File</a>
<a target="_blank" href="http://www.cnblogs.com/alandre/p/3604176.html">JSP2.0 的福利(标簽檔案)</a>