天天看點

Java精選筆記_自定義标簽

自定義标簽

自定義标簽入門

什麼是自定義标簽

自定義标簽可以有效地将HTML代碼與Java代碼分離,進而使不懂Java程式設計的HTML設計人員也可以編寫出功能強大的JSP頁面

JSP規範中定義了多個用于開發自定義标簽的接口和類,它們都位于javax.servlet.jsp.tagext包中

JSP自定義标簽是使用者定義的JSP語言元素,可以看成是一種通過标簽處理器生成基于XML腳本的方法。

自定義标簽在功能上和邏輯上都與JavaBean類似,都是一組可重用的元件代碼。相較于JavaBean,自定義标簽可以使Web開發者可以完全從Java程式設計中脫離開來,專注于頁面顯示和格式上面去,是以具有廣闊的發展前景。

自定義标簽的構成

一個自定義标簽一般由JavaBean、标簽庫描述、标簽處理器、web.xml檔案配置、标簽庫聲明等元素所構成。

自定義标簽聲明

當JSP頁面中引用自定義标簽時,用來在頁面上對自定義标簽進行聲明的。

taglib編譯指令的作用主要是定義一個标簽庫路徑及其字首

<%@ taglib="URLToTagLibrary" prefix="tagPrefix"%>

标簽庫描述符檔案

是一個描述标簽庫的XML文檔

TLD包含有關整個庫以及庫中包含的每一個标簽的資訊。它把自定義标簽與對應的處理程式關聯起來。TLD檔案名稱必須擴充名為.tld。

TLD檔案存儲在Web子產品的WEB-INF目錄下或者子目錄下,并且一個标簽庫要對應一個标簽庫描述檔案,而在一個描述檔案中可以包含多個自定義标簽的聲明

<taglib>子元素

元素

說明

元素

說明

<tlib-version>

用于設定标簽庫版本

<small-icon>

用于設定标簽庫的可選小圖示

<jsp-version>

用于設定标簽庫要求的JSP規範版本

<large-icon>

用于設定标簽庫的可選大圖示

<short-name>

用于設定該标簽庫的助記名

<description>

用于設定标簽庫的描述資訊

<uri>

唯一辨別該标簽庫的URI

<listener>

用于設定标簽庫的監聽器類

<display-name>

用于設定标簽庫顯示的可選名

<tag>

用于設定标簽庫的具體标簽

真正用來查找标簽庫中具體标簽的是<tag>元素

<tag>子元素

元素

說明

元素

說明

<name>

用于設定标簽的唯一名稱

<small-icon>

用于設定标簽的可選小圖示

<tag-class>

用于設定标簽處理器的完全限定名

<large-icon>

用于設定标簽的可選大圖示

<tei-class>

用于設定腳本變量資訊的子類名稱

<description>

用于設定标簽的描述資訊

<body-content>

用于設定标簽的正文内容類型

<variable>

用于設定标簽的腳本變量資訊

<display-name>

用于設定标簽顯示的可選名

<attribute>

用于設定标簽的屬性資訊

标簽處理器

把自定義标簽的主體和屬性轉變為HTML代碼的實際工作,是由标簽處理器來完成的。

标簽處理器也叫标簽處理類,它是一個Java類。當JSP容器編譯自定義标簽時,就會需要使用标簽處理器類的執行個體。

标簽處理器雖然是一個Java類,但不僅僅是一個普通的Java類,在定義時需要滿足特殊的要求。開發的标簽處理類必須實作Tag或者BodyTag接口類(它們包為javax.servlet.jsp.tagext)

BodyTag接口是繼承了Tag接口的子接口。如果建立的自定義标簽不帶體式,可以實作Tag接口,但是如果建立的自定義标簽帶體,則需要實作BodyTag接口。

Tag接口類方法

方法名

方法描述

setPageContext(PageContext pc)

設定目前頁面的上下文

setParent(Tag t)

設定這個标簽處理類的父類

getParent()

獲得父類

doStartTag()

處理這個執行個體中的開發标簽

doEndTag()

處理這個執行個體中的結束标簽

release()

由标簽處理類引起,來釋放狀态

BodyTag子接口類重新定義了兩個新方法

方法名

方法描述

setBodyContent(BodyContent b)

為體中代碼作初始化

doInitBody()

為标簽體中的内容設定屬性

在标簽處理器中定義了标簽處理方法doStartTag()和doEndTag(),這兩個方法分别在标簽開始和結束時執行處理和輸出動作。

這兩個方法都要求分别傳回一個狀态碼,通知JSP容器歲自定義标簽的處理結果及整個JSP頁面的運作狀态

狀态碼一共有四種

生命周期

生成servlet需要建立标簽處理器類的一個執行個體

初始化标簽處理器,是servlet獲知其存在性

如果标簽具有屬性,屬性的取值通過處理器提供setter方法傳入到對象

調用标簽處理器的doStrartTag()方法并傳回一個整數值

标簽體被評估或忽略後調用标簽處理器的doEndTag()方法

調用标簽處理器的release()方法銷毀标簽處理器執行個體

自定義标簽的開發步驟

1、編寫标簽處理器

開發自定義标簽的核心任務就是要編寫作為标簽處理器的Java類。

傳統标簽開發

實作javax.servlet.jsp.tagext.Tag接口

調用doStartTag()方法

簡單标簽開發

實作javax.servlet.jsp.tagext.SimpleTag接口

調用doTag()方法

2、編寫标簽庫描述符(tld)檔案

要想讓JSP引擎在遇到自定義标簽時,能找到其所對應的标簽處理器類,還必須編寫一個标簽庫描述符(Tag Library Descriptor)檔案,簡稱TLD檔案。

一個标簽處理器類要想被JSP容器找到并調用,必須在TLD檔案中進行注冊, 一個TLD檔案中可以注冊多個标簽處理器類,每個自定義标簽的注冊名稱不能相同,同一個TLD檔案中注冊的多個标簽處理器類就形成了一個自定義标簽庫。

TLD檔案是基于XML檔案的,其内容的編寫需要遵循XML文法規範。

3、在JSP頁面導入和使用自定義标簽

TLD檔案編寫完成後,就可以在JSP檔案中使用自定義标簽。在使用自定義标簽之前,首先需要使用taglib指令來引入TLD檔案

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

uri屬性用于指定引用的是哪一個TLD檔案,它應該和要引入的TLD檔案中<uri>元素的值保持一緻。

prefix屬性用于為引入的TLD檔案指定一個“引用代号”,在使用這個标簽庫中注冊的自定義标簽時都需要加上這個“引用代号”作為字首。prefix屬性的值可以是任意的,但不能和其它taglib指令中的prefix屬性值重複,而且需要遵循XML名稱空間的命名約定。

自定義标簽的格式

空标簽

<prefix:tagname />            // 格式1

<prefix:tagname></prefix:tagname>       // 格式2

帶标簽體的标簽

<prefix:tagname>body</prefix:name>

帶屬性的标簽

<prefix:tagname  attrname1 = "attrvalue1" [attrname2 = "attrvalue2" …]>
       [body]
</prefix:tagname>      

嵌套标簽

<prefix:tagname>
     <prefix:nestedtagname>
           [body]
     </prefix:nestedtagname>
</prefix:tagname>      

傳統标簽

實作Tag接口的标簽稱為傳統标簽

Tag接口

是所有傳統标簽的父接口,它定義了四個int類型的靜态常量和六個抽象方法。

定義了JSP頁面與标簽處理器之間的通信規則,當JSP容器将JSP頁面翻譯成Servlet源檔案時,如果遇到JSP标簽,會建立标簽處理器類的執行個體對象,然後依次調用标簽處理器的setPageContext()方法、setParent()方法、doStartTag()方法、doEndTag()方法和release()方法,是以,在實作Tag接口時,需要對這些抽象方法進行實作。

靜态常量

EVAL_BODY_INCLUDE

doStartTag()方法的傳回值,表示标簽體會被執行

SKIP_BODY

doStartTag()方法的傳回值,表示标簽體不被執行

EVAL_PAGE

doEndTag()方法的傳回值,表示标簽後面餘下的JSP頁面繼續執行

SKIP_PAGE

doEndTag()方法的傳回值,表示标簽後面餘下的JSP頁面不被執行

抽象方法

void setPageContext(PageContext pc)

JSP容器執行個體化标簽處理器後,調用setPageContext()方法将JSP頁面的内置對象pageContext對象傳遞給标簽處理器,标簽處理器可以通過pageContext對象與JSP頁面進行通信

void setParent(Tag t)

調用setPageContext()方法後,JSP容器會調用setParent()方法将目前标簽的父标簽處理器對象傳遞給目前标簽處理器,如果目前标簽沒有父标簽,則傳遞給setParent()方法的參數為null

Tag getParent()

傳回目前标簽的父标簽處理器對象,如果目前标簽沒有父标簽則傳回null

int doStartTag()

當JSP容器解析到自定義标簽的開始标簽時,會調用doStartTag()方法,該方法可以傳回EVAL_BODY_INCLUDE和SKIP_BODY兩個常量,如果使用Tag的子接口BodyTag,還可以使用BodyTag.EVAL_BODY_BUFFERED常量。

int doEndTag()

當JSP容器解析到自定義标簽的結束标簽時,會調用doEndTag()方法,該方法可以傳回EVAL_PAGE和SKIP_PAGE兩個常量

void release()

JSP容器在标簽處理器對象被作為垃圾回收之前調用release()方法,以便釋放标簽處理器所占用的資源

IterationTag接口

對标簽體的内容進行重複處理

它繼承自Tag接口,在Tag接口基礎上新增了一個EVAL_BODY_AGAIN常量和一個doAfterBody()方法

EVAL_BODY_AGAIN常量

是doAfterBody()方法的傳回值,如果doAfterBody()方法傳回該常量,JSP容器會把标簽體的内容重複執行一次。

doAfterBody()方法

JSP容器在每次執行完标簽體後會調用doAfterBody()方法,該方法可以傳回常量SKIP_BODY和EVAL_BODY_AGAIN。如果方法傳回SKIP_BODY常量,JSP容器會去執行代表結束标簽的doEndTag()方法,如果傳回EVAL_BODY_AGAIN,則重複執行标簽體。

BodyTag接口

對标簽體的内容進行處理以後再向浏覽器輸出

它繼承自IterationTag接口,在IterationTag接口基礎上新增了兩個方法和一個靜态常量

EVAL_BODY_B9UFFERED常量

如果标簽處理器類實作了BodyTag接口,它的doStartTag()方法除了可以傳回SKIP_BODY和EVAL_BODY_INCLUDE常量之外,還可以傳回EVAL_BODY_BUFFERED常量。

  當doStartTag()方法傳回EVAL_BODY_BUFFERED常量時,JSP容器将會建立一個javax.servlet.jsp.tagext.BodyContent對象,使用該對象來執行标簽體。

setBodyContent(BodyContent b)方法

當且僅當doStartTag()方法傳回EVAL_BODY_BUFFERED常量時,JSP容器才會調用setBodyContent()方法,通過該方法将BodyContent對象傳遞給标簽處理器類使用。

doInitBody()方法

JSP容器在調用setBodyContent()方法後會調用doInitBody()方法來完成一些初始化工作,該方法的調用在标簽體執行之前。其中,最重要的是setBodyContent()方法。

BodyContent類

是JspWriter類的子類,它在JspWriter的基礎上增加了一個用于存儲資料的緩沖區,當調用BodyContent對象的方法寫資料時,資料将被寫入到BodyContent内部的緩沖區中。

當标簽處理器類的doStartTag()方法傳回EVAL_BODY_BUFFERED常量時,JSP容器會建立一個BodyContent對象,然後調用該對象的write()方法将标簽體的内容寫入BodyContent對象的緩沖區中。

在BodyContent類中定義了一些用于通路緩沖區内容的方法

String getString()

以字元串的形式傳回BodyContent對象緩沖區中儲存的資料

Reader getReader()

傳回一個關聯BodyContent對象緩沖區中資料的Reader對象,通過Reader對象可以讀取緩沖區中的資料

void clearBody()

用于清空BodyContent對象緩沖區中的内容

JspWriter getEnclosingWriter()

用于傳回BodyContent對象中關聯的JspWriter對象。當JSP容器建立BodyContent對象後,PageContext對象中的“out”屬性不再指向JSP的隐式對象,而是指向新建立的BodyContent對象。同時,在BodyContent對象中會用一個JspWriter類型的成員變量enclosingWriter記住原來的隐式對象,getEnclosingWriter()方法傳回的就是原始的JSP隐式對象

writerOut(Writer out)

用于将BodyContent對象中的内容寫入到指定的輸出流

簡單标簽

實作SimpleTag接口的标簽稱為簡單标簽

由于傳統标簽在使用三個标簽接口來完成不同的功能時,顯得過于繁瑣,不利于标簽技術的推廣。

SimpleTag接口與傳統标簽接口最大的差別在于:SimpleTag接口中隻定義了一個用于處理标簽邏輯的doTag方法,該方法用于取代傳統标簽接口中定義的doStartTag()、doEndTag()和doAfterBody()等方法。

  doTag()方法在JSP引擎執行自定義标簽時調用,并且隻被調用一次,那些使用傳統标簽接口所能完成的功能,都在doTag()方法體内完成。

簡單标簽API

SimpleTag接口

是所有簡單标簽處理器的父接口,它共定義了5個方法

void setJspContext(JspContext pc)

用于将JSP頁面的内置對象pageContext對象傳遞給标簽處理器,标簽處理器可以通過pageContext對象與JSP頁面進行通信。JSPContext類是PageContext類的父類,其中定義了一些不依賴于Servlet運作環境的方法,setJspContext()方法接收的參數類型為JspContext,是為了便于将簡單标簽擴充應用到非Servlet運作環境中

void setParent(JspTag parent)

用于将目前标簽的父标簽處理器對象傳遞給目前标簽處理器,如果目前标簽沒有父标簽,JSP容器不會調用這個方法

JspTag getParent()

傳回目前标簽的父标簽處理器對象,如果目前标簽沒有父标簽則傳回null

void setJspBody(JspFragment jspBody)

用于把代表标簽體的JspFragment對象傳遞給标簽處理器對象

void doTag()

用于完成所有的标簽邏輯,包括輸出、疊代、修改标簽體内容等。在doTag()方法中可以抛出javax.servlet.jsp.SkipPageException異常,用于通知JSP容器不再執行JSP頁面中位于結束标簽後面的内容,這等效于在傳統标簽的doEndTag()方法中傳回SKIP_PAGE常量 

JspFragment類

它的執行個體對象代表JSP頁面中一段JSP片段,但是這段JSP片段中不能包含JSP腳本元素

JSP容器在處理簡單标簽的标簽體時,會把标簽體内容用一個JspFragment對象表示,并調用标簽處理器對象的setJspBody()方法将JspFragment對象傳遞給标簽處理器對象,标簽開發者可以根據需要調用JspFragment對象的方法來決定是否輸出标簽體、或者循環多次輸出标簽體等

兩個方法

JspContext getJspContext()

用于傳回代表調用頁面的JspContext對象

void invoke(Writer out)

用于将标簽體内容寫入到指定的輸出流對象out 中,如果調用該方法時傳入的參數為null,JSP容器會将标簽内容寫入到JspContext.getOut()方法傳回的輸出流對象中。

SimpleTagSupport類

為了簡化簡單标簽處理器的編寫

該類實作了SimpleTag接口,它内部使用成員變量jspContext和jspBody引用了JSP容器傳入的JspContext對象和JspFragment對象,并且提供了兩個方法來傳回這兩個對象的引用

JspContext getJspContext()

用于傳回代表調用頁面的JspContext對象

JspFragment getJspFragment()

用于傳回代表标簽體的JspFragment對象

控制是否執行标簽體内容

簡單标簽使用doTag()方法完成判斷,如果使用者登入,則顯示使用者名稱,如果使用者沒有登入,則可以抛出javax.servlet.jsp.SkipPageException異常,用于通知JSP容器不再執行标簽體内容,這等效于在傳統标簽的doEndTag()方法中傳回SKIP_PAGE常量。

控制是否執行JSP頁面的内容

假設一個網站要求隻能通過本網站中的超連結來通路某些JSP頁面時,如果直接通路這些JSP頁面或者通過非本網站的超連結來通路這些JSP頁面,那麼被通路的JSP頁面應該停止執行其中的内容,此現象稱為防盜鍊。

簡單标簽的屬性

如果多個JSP頁面都需要防盜鍊功能,則需要将使用者的通路請求重定向到不同的頁面。

  這時,為了提高标簽的靈活性和複用性,在JSP頁面使用自定義标簽時,可以通過設定屬性為标簽處理器傳遞參數資訊。

例如,可以為<itcast:antiHotLinking />标簽增加一個url屬性,通過該屬性指定重定向的頁面

<itcast:antiHotLinking url = "/chapter09/index.html" />

    要想為自定義标簽設定屬性,通常需要完成兩件任務,具體如下:

1、在标簽處理器類中,為每一個屬性定義對應的成員變量并定義setter()方法

自定義标簽每個屬性都必須按照JavaBean的屬性定義方式,預設情況下,自定義标簽的屬性值為靜态文本,當setter()方法的參數為String類型時,JSP容器會直接将屬性值作為一個字元串傳遞給該方法。如果setter()方法中的參數類型為基本資料類型,JSP容器在調用setter()方法之前會先将屬性值進行轉換。

例如在JSP頁面中使用了如下所示的标簽:

<itcast:test attr = "123" />

而标簽處理器類中定義的setter方法如下所示:

public void setAttr(int attr)

當JSP容器在處理attr屬性時,會先調用Integer. value("123")方法将字元串“123”轉換為整數123,之後再作為參數傳遞給标簽處理器類的setAttr()方法。

2、在TLD檔案中聲明每個标簽的屬性資訊

在TLD檔案中,<tag>标簽有一個子元素<attribute>用于描述自定義标簽的屬性,自定義标簽的每個屬性都必須要有一個對應的<attribute>元素

在<attribute>元素中還包含了一些子元素

description

用于描述屬性的描述資訊

name

用于指定屬性的名稱,屬性名大小寫敏感,且不能以jsp、_jsp、java、和sun開頭

required

用于指定在JSP頁面調用自定義标簽時是否必須設定這個屬性。其取值包括true和false,true表示必須設定,false表示設不設定均可。預設值為false

rtexprvalue

rtexprvalue是runtime expression value(運作時表達式)的簡稱,用于指定屬性的值為靜态還是動态。其取值包括true和false,true表示屬性值可以為一個動态元素,比如一個腳本表達式<%=value%>,false表示屬性值隻能為靜态文本值,比如“abc”。預設值為false

type

用于指定屬性值的類型