前面一篇博客介紹了自定義標簽的傳統標簽使用方式,但是我們會發現,使用傳統標簽非常的麻煩,而且接口還多,現在傳統標簽基本都沒用了,除了一些比較久的框架。Sun公司之后推出了一個新的標簽使用方式,稱之為簡單標簽。簡單標簽也作為JSP2.0以后的標准標簽使用方式,下面是一張傳統標簽和簡單標簽的對比,可以看到簡單標簽真的簡化了不少:
與傳統標簽的使用步驟是類似的,需要滿足以下兩個步驟:
① 編寫一個實現SimpleTag接口(實際上我們更常的是繼承SimpleTagSupport接口的實現類從而免於覆寫所有的方法)的Java類,這個Java類也稱為標簽處理器類。
② 編寫標簽的TLD文件,用於指定標簽的URI和對標簽的聲明描述,這一點和EL函數時一樣的,TLD文件必須放置在web應用下的【WEB-INF】文件中,可以是除【classes】和【lib】目錄以外的任何子目錄中。TLD文件的模板可以從【Tomcat】--->【webapps】--->【examples】--->【WEB-INF】--->【jsp2】中有一個“jsp2-example-taglib.tld”文件復制首尾和其中的<Tag>標簽。
注:在TLD文件中我們使用<tag>標簽來對每一個自定義標簽的Java類進行描述,其中每個<tag>標簽下還需要指定<body-content>的值,這個是描述標簽體的類型:對於簡單標簽使用:“empty”(無標簽體內容時) 或“scriptless” (有標簽體內容時)。
注意:簡單標簽在JSP頁面中,標簽體內容中是不允許使用JSP腳本的。
與傳統標簽需要使用doStartTag()方法和doEndTag()方法不同,簡單標簽只需要doTag()方法來處理標簽,所有的標簽體內容邏輯,迭代,是否執行標簽體內容等等都只需要調用doTag()方法即可。因此簡單標簽能和傳統標簽的bodyTagSupport能有一樣的功能,但是生命周期方式更簡單並且只有一個接口。
下圖是簡單標簽SimpleTag接口的所有方法:
其中除了getParent方法以外,其他都是生命周期方法,這一點和傳統標簽類似。簡單自定義標簽的生命周期順序為:① 創建自定義標簽的實例對象 ---> ② setJspContext方法 ---> ③ setJspBody方法(如果有標簽體才調用) ---> ④ setParent方法(如果有父類自定義標簽才調用)---> ⑤ doTag方法 ---> ⑥ 該自定義標簽的實例對象將被垃圾回收器回收(與傳統自定義標簽不同,簡單標簽的標簽處理器類對象不會緩存)。
下面簡單地介紹下這幾個生命周期方法:
setJspContext(JspContext pc)方法,JSP容器對簡單標簽進行實例化對象后,通過調用這個方法,將pageContext對象(pageContext是JspContext的子類)傳入標簽處理器類從而使得在標簽處理器類處理標簽和標簽體時(doTag)能通過這個對象獲取其他八大web對象。
setJspBody(JspFragment jspBody)方法,是將標簽體內容封裝為一個JspFragment對象傳入給標簽處理器類,也就是說JspFragment對象就代表標簽體的內容。如果想要執行標簽體,只需要調用JspFragment對象的invoke(Writer out)方法傳入一個輸出流對象即可,或者如果往invoke方法中傳入的參數為null的話,默認參數為JspWriter對象,等效於invoke(this.getJspContext.getOut)。如果不調用invoke方法,那么就不會執行標簽體內容,另外可以隨意調用多次invoke方法。另外如果簡單標簽沒有標簽體內容,那么setJspBody不會被調用。
setParent方法,只有在該自定義標簽嵌入在另一個自定義標簽中才會調用,記住父標簽必須也是自定義標簽才可以,如果父標簽只是簡單的HTML標簽那么該setParent方法不會被調用。
doTag()方法,由JSP容器來調用,通常是由我們開發人員來覆寫這個方法的實現,我們要使用標簽來完成任何功能的實現都在這個方法中。如果我們不想執行這個標簽后面余下的JSP頁面,那么只需要在這個方法內拋出一個“SkipPageException”異常即可。
如果要想直接實現SimpleTag接口,那么在這個實現類中一定要有一個無參的構造函數。通常我們選擇繼承SimpleTag接口的實現類,也就是SimpleTagSupport類。繼承SimpleTagSupport類的話,繼承的子類中還可以使用SimpleTagSupport類給子類提供的方法獲取pageContext對象或者JspFragment對象:
例1:使用簡單標簽來控制是否執行標簽體內容
編寫標簽處理器類:

1 package com.fjdingsd.simpletag; 2 public class ShowTagBodyOrNot extends SimpleTagSupport { 3 @Override 4 public void doTag() throws JspException, IOException { 5 6 JspFragment fragment = this.getJspBody(); 7 fragment.invoke(this.getJspContext().getOut()); 8 //上面的代碼等效於fragment.invoke(null); 9 } 10 }
在TLD文件中聲明描述標簽:

1 <?xml version="1.0" encoding="UTF-8" ?> 2 <taglib xmlns="http://java.sun.com/xml/ns/j2ee" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd" 5 version="2.0"> 6 <description>A tag library exercising SimpleTag handlers.</description> 7 <tlib-version>1.0</tlib-version> 8 <short-name>SimpleTagLibrary</short-name> 9 <uri>simpletag</uri> 10 11 <tag> 12 <name>showbody</name> 13 <tag-class>com.fjdingsd.simpletag.ShowTagBodyOrNot</tag-class> 14 <body-content>scriptless</body-content> 15 </tag> 16 17 </taglib>
在JSP頁面中導入taglib指令:
<%@ taglib uri="simpletag" prefix="simple" %>
最后就可以在JSP頁面的主體中使用剛才定義的簡單標簽了:

1 <simple:showbody> 2 銀魂 3 </simple:showbody>
在瀏覽器中觀察:
分析:能顯示的原因,是因為在這個標簽中,將標簽體封裝成JspFragment對象,並且通過這個對象調用了invoke()方法,即執行標簽體內容,如果我們想控制標簽體內容不輸出顯示的話,只要不調用invoke()方法即可,修改例1的標簽處理器類:

1 public class ShowTagBodyOrNot extends SimpleTagSupport { 2 @Override 3 public void doTag() throws JspException, IOException { 4 5 JspFragment fragment = this.getJspBody(); //這句代碼,或者甚至這個方法沒有都可以,都能控制標簽體內容不執行,要想執行必須要有JspFragment.invoke方法 6 7 } 8 }
其他TLD文件等等內容都不變,則瀏覽器中不會對這個標簽顯示任何內容。
例2:使用簡單標簽來控制標簽體內容執行次數
編寫標簽處理器類:

1 package com.fjdingsd.simpletag; 2 public class LoopTagBody extends SimpleTagSupport { 3 @Override 4 public void doTag() throws JspException, IOException { 5 JspFragment fragment = this.getJspBody(); 6 for(int i=0;i<3;i++) { 7 fragment.invoke(null); //標簽體執行3次 8 } 9 } 10 }
在TLD文件中描述(這里省略首尾,詳細內容請看例1):

1 <tag> 2 <name>loopbody</name> 3 <tag-class>com.fjdingsd.simpletag.LoopTagBody</tag-class> 4 <body-content>scriptless</body-content> 5 </tag>
在JSP頁面中導入taglib指令(此處略)后,在JSP頁面的主體中使用剛才定義的簡單標簽:

1 <simple:loopbody> 2 神樂 <br> 3 </simple:loopbody>
在瀏覽器中觀察:
例3:使用簡單標簽來修改標簽體內容再輸出(這里將標簽體內容中所有字母變為大寫)
分析:要想使用簡單標簽來修改標簽體內容再輸出,需要先獲取標簽體對象(JspFragment),我們知道要想執行標簽體必須調用invoke(Writer out)方法,但是如果我們使用非緩沖的Writer輸出流,那么就直接輸出給瀏覽器了,無法達到修改目的,因此我們應該使用帶有緩沖流的字符流對象,將標簽體內容通過invoke方法先存入字符流,再從字符流中取出修改最后再輸出給瀏覽器。
編寫標簽處理器類:

1 package com.fjdingsd.simpletag; 2 public class ModifyTagBody extends SimpleTagSupport { 3 @Override 4 public void doTag() throws JspException, IOException { 5 JspFragment fragment = this.getJspBody(); 6 StringWriter writer = new StringWriter(); 7 fragment.invoke(writer); 8 9 String tagContent = writer.toString().toUpperCase(); 10 this.getJspContext().getOut().write(tagContent); 11 } 12 }
另:使用StringWriter緩沖流是因為StringWriter能夠獲得緩沖區中的值:
在TLD文件中描述(這里省略首尾,詳細內容請看例1):

1 <tag> 2 <name>modifybody</name> 3 <tag-class>com.fjdingsd.simpletag.ModifyTagBody</tag-class> 4 <body-content>scriptless</body-content> 5 </tag>
在JSP頁面中導入taglib指令(此處略)后,在JSP頁面的主體中使用剛才定義的簡單標簽:

1 <simple:modifybody> 2 long live sd 3 </simple:modifybody>
在瀏覽器中觀察:
例4:使用簡單標簽來控制便簽后余下的JSP頁面是否顯示
若想使用簡單標簽來控制便簽后余下的JSP頁面不顯示,只需要在標簽處理器類的doTag方法中拋出“SkipPageException”異常即可。
編寫標簽處理器類:

1 package com.fjdingsd.simpletag; 2 public class HideRemainJSP extends SimpleTagSupport { 3 @Override 4 public void doTag() throws JspException, IOException { 5 6 throw new SkipPageException(); 7 } 8 }
在TLD文件中描述(這里省略首尾,詳細內容請看例1):

1 <tag> 2 <name>hidepage</name> 3 <tag-class>com.fjdingsd.simpletag.HideRemainJSP</tag-class> 4 <body-content>empty</body-content> 5 </tag>
在JSP頁面中導入taglib指令(此處略)后,在JSP頁面的主體中使用剛才定義的簡單標簽:

1 <simple:hidepage/> 2 3 <!DOCTYPE HTML> 4 <html> 5 <head> 6 <title>My JSP 'simpletagdemo.jsp' starting page</title> 7 </head> 8 9 <body> 10 。。。 11 </body> 12 </html>
結果:我在JSP頁面最開始的地方使用了剛才自定義的簡單標簽,控制該標簽后的頁面不再執行,因此瀏覽器訪問該頁面中將只會看到空白頁面,包括頁面源碼也是為空。