Java for Web學習筆記(三五):自定義tag(3)TLDS和Tag Handler
JSTL的TLD
這是JSTL採用的方式。TLD(Tag Library Descriptor)描述tag和function,以及具體執行的java程式碼tag handler。Tag Handler是javax.servlet.jsp.tagext.Tag或javax.servlet.jsp.tagext.SimpleTag的實現。web容器使用TLD將jsp中的tag對映到執行的java程式碼。之前,我們一直在學習使用JSTL的tag,我們來看看這些TLD是怎麼寫的。C tag的TLD可以解開javax.servlet.jsp.jstl的jar包,位於/META-INF/c.tld。
整體描述
<?xml version="1.0" encoding="UTF-8" ?> <!-- ... Copyright (c) 2010 Oracle and/or its affiliates. All rights reserved. ...--> <taglib xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd" version="2.1"> <description>JSTL 1.2 core library</description> <display-name>JSTL core</display-name> <tlib-version>1.2</tlib-version> <short-name>c</short-name> <uri>http://java.sun.com/jsp/jstl/core</uri> ... ... </taglib>
這裡是XML的標準宣告,其中Schema在web-jsptaglibrary_2_1.xsd中定義,需要注意的是TLD檔案是有嚴格的先後順序。頭五個根元素描述TLD的整體資訊:
- <description>和<display-name>是可選的,用於在IDE中的XML工具顯示,和真正的TLD內容沒什麼關係,可有多個,用於不同語言的顯示。
- <icon>位於<display-name>之後<tlib-version>之前,也是可選的,實際沒什麼用途。
- <tlib-version>必須要提供,版本只能是數字,並用.分隔。
- <short-name>必選,給出推薦和預設的tag字首,不允許有空格,不允許數字或者下劃線開頭
- <uri>是可選,但應當給出。如果不提供,則在<jsp-config>(web.xml)中必須加入<taglib>來描述這個tag library,所以最好且應該提供。之前以及提過,這個連結不需要也通常不會真實存在的。
定義Validators和Listeners
接下來,我們看到:
<validator>
<description>
Provides core validation features for JSTL tags.
</description>
<validator-class>
org.apache.taglibs.standard.tlv.JstlCoreTLV
</validator-class>
</validator>
在編譯的時候使用javax.servlet.jsp.tagext.TagLibraryValidator類來驗證,確保正確使用tag lib,如<c:param>的格式正確。validator複雜且很少在自定義tag中使用,略過。
緊接著的是Listeners,如ServletContextListener,HttpSessionListener,但是它的使用極為罕見,同樣略過。
定義Tags
如何定義Tags是關鍵,一個TLD中可以定義多個tag,下面是c tag的第一個。為了方便閱讀,我們將解析也放進裡面。
<tag>
<!-- 和taglib一樣有0~n個<description>,<display-name>,<icon>,通常只需要<description> -->
<description>
Catches any Throwable that occurs in its body and optionally exposes it.
</description>
<!-- 定義tag名字為<c:catch>,c在之前的<short-name>中定義,catch在這裡的<name>定義。 -->
<name>catch</name>
<!-- 定義這個tag的處理類(Tag或者SimpleTag的繼承) -->
<tag-class>org.apache.taglibs.standard.tag.common.core.CatchTag</tag-class>
<!-- 例子中沒有給出可選的<tei-class>,是javax.servlet.jsp.tagext.TagExtraInfo的繼承,用於校驗tag的屬性,確保使用的正確。這個偶爾有定義,但不常見。在C tag中僅<c:import>和<c:forEach>提供了擴充套件資訊類。 -->
<!-- body-content支出tag的content內容:-->
<!-- 1、empty:無content -->
<!-- 2、scriptless:可以是template,EL,JSPtag,但不能是scriptlet或expressions -->
<!-- 3、JSP:只要JSP允許的都可以(當然declaration例外) -->
<!-- 4、tagdependent:由tag本身,而不是web容器來計算tag的內容,通常是其他語言,如SQL,XML,或者加密資料 -->
<body-content>JSP</body-content>
<!-- 0~N個<variable>,本例沒有,實際上在JSTL TLDs中都沒有。這是用來提供tag的結果的各種變數資訊。什麼是variable?我們通過pageContext.setAttribute/getAttribute設定或獲取某個值,這就是變數。不通過variable,我們也可以通過EL來獲取。如果提供了variable,我們除了EL外,還可以在java程式碼中使用這些變數。既然在JSP中,java程式碼不建議使用,在JSTL TLDs中都沒有使用variable,一般而言,我們沒必要使用。子引數依次包括:-->
<!-- <description> -->
<!-- <name-given> 變數的名字 -->
<!-- <name-from-attribute> 決定變數名字的屬性,這和<name-given>相互衝突,只能二選一。String,不能是EL等在執行時計算。 -->
<!-- <variable-class> 變數的型別 -->
<!-- <declare> true表示變數是新,需要宣告;false表示已存在,只是修改值 -->
<!-- <scope> 預設是NESTED,即在tag內有效;AT_BEGIN表示變數tag內及之後有效;AT_END表示變數只在tag後有效,而tag內無效。 -->
<!-- 0~N個<attribute>,定義Tag的屬性-->
<attribute>
<!-- 先是<description>、<name> -->
<description>
Name of the exported scoped variable for the exception ...(略)
</description>
<name>var</name>
<!-- required預設是false,表示不強制要求 -->
<required>false</required>
<!-- rtexprvalue是runtime expression value:true表示允許EL這類執行時計算值,false則不允許,預設為false。 -->
<rtexprvalue>false</rtexprvalue>
<!-- <type>表示屬性的java類,預設為Object -->
<!-- <deferred-value>表明該屬性是一個延遲計算的EL,tag的結果將通過javax.el.ValueExpression返回,通常會返回Object,也可以通過type來精確定義 -->
<!-- <deferred-method>表明該屬性是一個延遲計算的EL,tag的結果將通過javax.el.MethodExpression返回。一般而言在method()中執行,也可以通過<method-signature>中定義更為精確的signature -->
<!-- <fragment>設定true時表示用jsp fragment,容器不進行計算,而是由handler進行計算。預設值為false。如果使用fragment,可以通過<jsp:attribute>來設定,而非xml的屬性,例子如下 -->
<!-- <myTags:doSomething> -->
<!-- <jsp:attribute name="someAttribute"> -->
<!-- Any <b>content</b> <fmt:message key="including.jsp.tags" />. -->
<!-- </jsp:attribute> -->
<!-- </myTags:doSomething> -->
</attribute>
<!-- <dynamic-attributes>預設值為false,如果設定為true,表示允許使用沒有定義的屬性。這通常在jsg tag的最終輸出為html tag的情況,這時handler需要實現javax.servlet.jsp.tagext.DynamicAttributes。Spring的form tag lib就使用動態屬性 -->
<!-- 0~1個<example>,用於給出tag使用的示範案例 -->
<!-- 0~n個<tag-extension>,提供給IDE的,對tag本身沒有任何影響。 -->
</tag>
定義Tag檔案
在TLDS中是可以使用0~n個tag檔案,定義在tag後面,如下:
<tag-file>
<!-- <description>、<display-name>和<icon>,其中description是必選 -->
<description>This tag outputs bar.</description>
<!-- name就是prefix之後的名字,和tag中一樣 -->
<name>foo</name>
<!-- tag檔案路徑,web應用中的/WEB-INF/tags或者jar中的/META-INF/tags -->
<path>/WEB-INF/tags/foo.tag</path>
<!-- 最後是<example>和<tag-extension> -->
</tag-file>
如果我們將tag檔案加入到jar中,就必須在TLD中定義;如果多個tag檔案有相同的namespace,也需在TLD中定義。
定義Functions
c tag中沒有functions定義的例子,但在fn tag中有,可以開啟fn.tld來學習。下面是當中的例子:
<function>
<!-- <description>, <display-name>, <icon>, <name>已經是老朋友了。 -->
<description>
Tests if an input string contains the specified substring.
</description>
<name>contains</name>
<!-- 該function對應的公共類全名 -->
<function-class>org.apache.taglibs.standard.functions.Functions</function-class>
<!-- 該function在公共類中對應的公共靜態方法 -->
<function-signature>boolean contains(java.lang.String, java.lang.String)</function-signature>
<!-- 接下來的<example>、<function-extension>和tag的<example>和<tag-extension>同義 -->
<example>
<!-- 這裡是個不好的示例,我們應該寫成<![CDATA[...]]> -->
<c:if test="${fn:contains(name, searchString)}">
</example>
</function>
定義Tag library Extensions
<taglib-extension>和tag已經function中的extension一樣,對tag或者container無實際作用,僅是支援IDE工具,我們不會使用到的。