JSP系列六:JSP自訂標籤

來源:互聯網
上載者:User

一,自訂標籤 :實現了特定介面的java類,封裝了java代碼編寫的預定義行為。

 * 在運行時,標籤被替換成相應的預定義java代碼。

 * JSP 自訂標籤提供了替代簡單的 JavaBean 和 Java 指令碼的方法。
  更好的是在 JSTL 中已存在一組已定義的標準的自訂標籤庫。

 * 目的在於將業務和表示邏輯分離,代碼的可重用性,可移植性。

二,標籤庫:按照功能或實現進行分組的自訂標籤的集合。

 1,API : javax.servlet.jsp.tagext包中的三個介面和2個類
  
 2,組成:
  * 標籤處理常式類
  * 標籤庫描述符檔案
  * 應用程式部署描述符
  * jsp檔案

 3,自訂標籤步驟:
  1,實現:實現tagext包定義的介面或類;
  2,配置:配置TLD檔案;
  3,關聯:配置web.xml檔案;
 
三,標籤處理常式類:一個自訂標籤的實作類別;

 1,標籤處理常式是在運行時期調用。必須實現或擴充javax.servlet.jsp.tagext包定義的三個Java介面中的一個。

  * Tag介面 : 標籤處理常式和JSP頁面的基本協議。定義了所有標籤處理常式的基本方法。
  * IterationTag介面:擴充Tag介面,控制對標籤體的重複處理。
  * BodyTag介面:繼承IterationTag介面,對標籤體中內容進行處理。

 2,介面的預設實現:TagSupport和BodyTagSupprt類:

  * TagSupport類為Tag和IterationTag介面的預設實現。支援簡單標籤和帶主體迭代的標籤。
  * BodyTagSupprt類為BodyTag介面的預設實現。支援需要訪問和操作標籤主體內容的標籤。

 3,實現Tag介面的標籤處理常式類的生命週期:

  1,setPageContext():在標籤處理器之前被JSP容器調用。設定標籤的頁面上下文,調用 setParent()方法設定該標籤的父標籤,沒有則設定為NULL;
    setParent()
 
  2, 設定標籤的屬性: 有屬性,調用setXxx方法設定標籤屬性,沒有屬性則跳過。
 
  3,doStartTag():返回EVAL_BODY_INCLUDE或SKIP_BODY
 
   EVAL_BODY_INCLUDE :輸出標籤體到當前的輸出資料流
 
   SKIP_BODY:忽略標籤體
 
  4,doEndTag(): 返回EVAL_PAGE或SKIP_PAGE
 
   EVAL_PAGE:執行頁面餘下部分
 
   SKIP_PAGE:忽略頁面餘下部分
 
  5,容器緩衝標籤處理器類執行個體,重複使用緩衝的標籤處理類執行個體
 
  6,release():釋放執行個體,
 
 4,實現IterationTag介面的標籤處理常式類的生命週期:
 
  1,setPageContext()
     setParent()
 
  2, 設定標籤的屬性
 
  3,doStartTag(): 返回EVAL_BODY_INCLUDE或SKIP_BODY
 
   EVAL_BODY_INCLUDE:
    執列標籤體(被執行一遍);
    調用doAfterBody()方法:返回EVAL_BODY_AFTER或SKIP_BODY;
 
     EVAL_BODY_AFTER: 跳轉到前面重新執列標籤體
     SKIP_BODY:
   
   SKIP_BODY:
 
  4,doEndTag() : EVAL_PAGE或SKIP_PAGE
 
   EVAL_PAGE:執行頁面餘下部分
   SKIP_PAGE:
 
  5,從1開始
 
  6,release()

 
 5,實現BodyTag介面的標籤處理常式類的生命週期:
 
  1,setPageContext()
    setParent()
 
  2, 設定標籤的屬性
 
  3,doStartTag(): 返回EVAL_BODY_BUFFERED或SKIP_BODY_INCLUDE,SKIP_BODY.
 
   EVAL_BODY_BUFFERED且標籤體不為null:
    setBodyContent():
     設定標籤處理器類的bodyContent屬性,提供了BodyContent對象,緩衝標籤體靜態內容和動態內容。
     調用doInitBody()為標籤體執行做準備
      跳轉到 執列標籤體:
   SKIP_BODY_INCLUDE : 直接跳轉到 執列標籤體:
 
   SKIP_BODY : 忽略標籤體,直接跳到 5
 
  4, 執列標籤體(被執行一遍後);
  
   調用doAfterBody()方法:返回EVAL_BODY_AFTER或SKIP_BODY;
 
     EVAL_BODY_AFTER: 跳轉到前面重新執列標籤體
     SKIP_BODY:
   
   SKIP_BODY:不重複執行
 
  5,doEndTag() : EVAL_PAGE或SKIP_PAGE
 
   EVAL_PAGE:執行頁面餘下部分
   SKIP_PAGE:
 
  6,從1開始
 
  7,release()

 6,TagSupport類:支援簡單標籤和帶主體迭代的標籤。
  

 7, BodyTagSupprt類:支援需要訪問和操作標籤主體內容的標籤。

  

 8,BodyContent類 :緩衝輸出內容。

  * JSP容器處理頁面簽建立JspWriter類執行個體,把所有輸出引入該執行個體中(靜態和動態內容等行為),
   如果JSP容器遇到的“自訂標籤實作類別”實現了BodyTag介面,就會臨時將所以緩衝在JspWriter執行個體中的所有輸出重新置放到BodyContent類執行個體中緩衝,直到自訂標籤結束。
   所以BodyContent也是JspWriter類的子類,而JSP標籤處理器也就可以通過該對象的getString()方法讀到標籤體中的內容了。

  public void flush()throws IOException    複寫JspWrite.flush()方法以便它總是產生溢出。重新整理寫入已失效,因為它沒有串連到將被寫入的實際輸出資料流中。
  public void clearBody()    重設BodyContent緩衝為空白。
  public Reader getReader()    返回Reader讀取體內容。
  public String getString()    返回標籤體中的內容。
  public void writeOut(Write w)    將體內容寫入指定輸出。
  public JspWrite getEnclosingWrite()    返回棧中下一個更高的寫入者對象(可能是另一個BodyContent對象)。
 
 9,JSP標籤處理器的生命週期
  1,初始化執行個體:建立標籤執行個體,調用所有的設定方法(setPageContext,setParent方法和所有屬性的設定方法)來初始化執行個體。
  2,調用doStartTag()方法將執行個體變數設為僅在當前調用的有效值。如果該方法正在處理元素的標籤體,就會調用doEndTag方法。
  3,標籤執行個體被重用,如果屬性有不同的值則調用對應的設定方法,重複2的操作。只有在具有相同屬性集合時,標籤處理器才將執行個體重用。
  4,使用release方法讓標籤處理器釋放內部佔有資源。

 10,注意:
  * 為"屬性"提供預設值。

  * 由於容器緩衝標籤處理器類執行個體,重複使用緩衝的標籤處理類執行個體。
    所以在改變類變數後,後面使用該類變數也會受影響。
    所以,要注意並發訪問的問題。
     所以,要每次重設調用的狀態。最佳地點是doStartTag方法中。

  * 標籤在調用期間絕不會調用release();

  * 不要在setBodyContent方法和doInitBody方法中使用BodyContent對象,只用於擷取該對象和做準備工作。

  * 對於實現BodyTag介面的自訂標籤,同時使用空標籤和不為空白的標籤,那就會拋出異常。因為空白標籤不會調用一些方法。

 
  
四,標籤庫描述符(TLD)檔案:TLD是由在JSP頁面中使用的標記所組成的庫,用於配置標籤;

 1,標籤庫描述符檔案是描述一個標籤庫的XML文檔,包含了標籤的名字,屬性,標籤處理類和標籤的屬性等資訊。
 2,副檔名為.tld,必須放在WEB-INF目錄下。  

<taglib version="2.0"
 xmlns="http://java.sun.com/xml/ns/j2ee"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee web-jsptaglibrary_2_0.xsd">
 
    <tlib-version>1.0</tlib-version>
    <jsp-version>1.2</jsp-version>
    <short-name>標籤的簡稱</short-name>

    <tag>
      <name>標籤的名字</name>
      <tag-class>標籤處理類完整名</tag-class>
      <body-content>標記主體是否可以嵌入內容,以及內容如何處理。不可以使用empty</body-content>
    </tag>

</taglib>

tag元素的配置說明:
 
<tag>
 <name>tagNmae</name>-----這個Tag的名字
 <tagclass>package.Class</tagclass>-----這個Tag是由那個類實現的(這個class可以在struts.jar包中找到)
 <bodycontent>empty</bodycontent>-----這個Tag可以直接結尾,不需要填寫內容
     這裡bodycontent有三個可選值
         jsp  :標籤體由其他jsp元素組成  
           如果其有jsp元素,那麼標籤會先解釋,然後將元素的實際值傳入。比如標籤體裡含有<%=attributeName%>這樣子的jsp元素,此時標籤會按attributeName的實際值是什麼就傳入什麼。這個是最常用的一個。
         empty :標籤體必須為空白  
           在引用這個Tag的時候,可以<bean:write bundle="attributeName" />,而不必<bean:write bundle="attributeName" ></bean:write>
         tagdependent : 由標籤解釋,不帶jsp轉換

 <attribute> -----這裡標識的是這個Tag的一個參數
  <name>attrName</name>--這個參數的名字
  <required>false</required>-----這個參數是否是必填,如果為true則必須寫這個參數,否則會報錯
  <rtexprvalue>true</rtexprvalue>------是說這個屬性的值是否可以接收運行時的運算式。rtexprvalue:"RUN-TIME EXPRESSION VALUE",是否可以動態賦值,在jsp中如value="<%=attributeName%>"
 </attribute>
</tag>

 

六,建立 JSP 頁面與標記庫之間的引用 :靜態引用與動態引用,關聯標籤;

 1,可以通過 Web 應用程式描述符(web.xml)聲明一個靜態引用,
 <?xml version="1.0" encoding="UTF-8"?>
 <web-app version="2.4"
  xmlns="http://java.sun.com/xml/ns/j2ee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
  http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">

  <jsp-config>
   <taglib>
    <taglib-uri>/myTags</taglib-uri>
    <taglib-location>/WEB-INF/MyTLD.tld</taglib-location>
   </taglib>
  </jsp-config>
 
 </web-app>

 然後,將 JSP 聲明加入到所有需要使用自訂標籤庫的頁面中:

 <%@ taglib uri="myTags" prefix="abc" %>

  注意指定的 uri 屬性與在 web.xml 檔案中指定的 taglib-uri 值相匹配。

 2,可以直接在頁面中聲明一個動態引用。

 只需在所有需要使用這個庫的頁面中加入一個 JSP 聲明即可:

 <%@ taglib uri="/WEB-INF/lib/DateTagLib.tld" prefix="abc" %>

  3,靜態引用與動態引用比較:

 在進行標記庫的靜態引用時,JSP 聲明必須查詢 web.xml 檔案以執行庫查詢。
 這意味著如果移動或者重新命名了庫,或者希望在 web.xml 檔案中加入更多的庫,
 就必須停止伺服器、更新 web.xml 檔案、然後重新啟動伺服器。
 動態方法讓 JSP 頁直接指向 TLD 位置,因而是在解釋 JSP 頁面時進行處理。

 靜態方法提供了頁面與庫的實際名和位置之間一定程度的非直接性,
 這可以為您提供一些改變這些屬性而不修改頁面的靈活性。另一方面,動態方法提供了更大的靈活性,
 讓您可以在運行時增加和移動標記聲明。如果您對動態方法感興趣,
 但是又擔心做了一些改變後、有可能要更新多個頁面的維護負擔,
 那麼您可以始終將 JSP 聲明放到一個單獨的 JSP 檔案中,並在每一個要訪問 Web 應用程式的自訂庫的頁面中加入這一頁。
 這使您具有在運行時只需要更新資訊一次就可以增加庫的靈活性。

七,開發協作的行為
 1,使用顯示的父子協作: 通過內層的子標籤為父標籤提供參數,以構成完整或補充的功能實現; 比方<jsp:include>動作元素中的子項目<jsp:param>.
  
  * 使用TagSupport.findAncestorWithClass(this, ParamParent.class)方法在子標籤中擷取父標籤的引用。
  * 調用父標籤的方法將子類的參數傳遞給父類。
  * 父標籤為實現了特定的介面,該介面中定義了子標籤中的參數如何傳遞給父標籤,供子標籤調用。
 
 2,通過變數使用隱式協作:
  通過設定JSP範圍的變數而隱式進行協作。一個行為將其處理結果作為某個jsp範圍中的變數,而另外一個行為則使用變數作為其輸入。例如迭代行為。

八,在標籤庫中使用監聽器:
 使用<Listener>元素放在<validator>後面在TLD中為自己的庫定義監聽器實現。
 當容器載入WEB應用程式的時候,會遍曆所有的TLD來尋找監聽器定義,並且註冊所找到的監聽器。
  
九,資料類型轉換

 1,JSP容器自動處理從文本值到屬性值的基礎資料型別 (Elementary Data Type)的轉換。
 2,使用PropertyEditor進行轉換:將文本值轉換為任何java資料類型。

 

十,異常處理

 如果JSP元素拋出一個異常,對它的預設處理是發送到一個錯誤頁面。
 但對於特殊標記(如檔案操作)處理必須作出顯示的處理。JSP1.2新介面TryCatchFinally介面專門來處理異常。
 如果嵌套行為體中的元素doStartTag(),doEndTag(),doInitBody()或doAfterBody方法中的任何一個拋出異常。
 容器會調用doCatch方法。我們可以在該方法中記錄問題,拋出自己的異常。頁面對其他部分的處理也會繼續。

 doFinally方法總是由容器調用。當在doStartTag方法拋出異常,就會無法建立PrintWriter對象輸出資訊,所以要在檢查null。

 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.