1. 程式人生 > >Java for Web學習筆記(三五):自定義tag(3)TLDS和Tag Handler

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工具,我們不會使用到的。