儘管JSTL已經有很豐富的功能
但依然有可能碰到找不到我們想要的功能的時候
又或者是公司內部有一套自訂的標籤程式庫來應付內部的流程
這些情形都是學習自訂標籤的理由
然而撰寫自訂標並不是容易的事,最好先確認其必要性再動手去做
先說明使用自訂標籤的方式,後面在介紹自訂標籤的製作方式
它的使用方式會有點像之前提過的 EL 函式
不過實際撰寫的結果是一個 tld 檔裡似乎無法同時描述自訂標籤與EL函式
先來個程式範例:
rand.tld=============
<?xml version="1.0" encoding="UTF-8" ?>
<taglib 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-jsptaglibrary_2_0.xsd"
version="2.0">
<tlib-version>1.2</tlib-version> <!-- 宣告標籤程式庫版本 -->
<short-name>randTag</short-name> <!-- 供開發工具使用的必要元素 -->
<uri>randTag</uri> <!-- taglib指令中使用的獨特名稱 -->
<tag>
<description>for test</description> <!-- optional, 但是可以用來幫助描述自訂標籤 -->
<name>myTag</name>
<tag-class>tw.vencs.DoTag</tag-class>
<body-content>empty</body-content>
<attribute>
<name>attr</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
</taglib>
DoTag.java=======
package tw.vencs;
import javax.servlet.jsp.tagext.SimpleTagSupport;
import javax.servlet.jsp.JspException;
import java.io.IOException;
public class DoTag extends SimpleTagSupport {
private String demoStr;
public void doTag() throws IOException, JspException{
getJspContext().getOut().write(response());
}
//設定標籤裡的屬性值的方法,本例中設定此標籤有一屬性名為 attr
public void setAttr(String demoStr){
this.demoStr = demoStr;
}
//內部方法
public String response(){
String str = "What u need is " + demoStr;
return str;
}
}
show.jsp=========
<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="utf-8"%>
<%@ taglib prefix="test" uri="randTag" %> <!-- uri 填上所要用的 tld 檔的uri -->
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>test</title>
</head>
<body>
<test:myTag attr="name" />
</html>
tld 檔裡的 <tag> 元素是這次的重點之一
裡面的 <name> 元素設定我們使用的標籤名稱,例如JSP裡的<test:myTag attr="name"/>
<tag-class> 設定標籤所使用的程式碼
<body-content>設定成empty表示這個標籤不能夠有東西在主體裡
<attribute>則設定此標籤所擁有的屬性
它內部的<name>元素則指定屬性的名稱
<required>設定成 true 表示使用此自訂標籤就必須為它加上這個屬性
<rtexprvalue> 負責設定屬性的判別方式,例如 true 表示值可以不為字串實字
tld 檔的放置位置限定在WEB-INF下,或是WEB-INF的子目錄裡
自訂標籤的處理由繼承 SimpleTagSupport 的Java類別來做
它裡面需要實作的方法:有實際處理工作的doTag()和設定屬性值的方法(如本例的setAttr())
設定屬性值的方法的命名方式依照 JavaBean 規格
之前在撰寫EL函式時會宣告<function-signature>
但因為自訂標籤的使用方法只會有 doTag(),所以不會在DD中宣告方法名稱
JSP 檔的 taglib 指令有需必須要做的設定
container會比對 tld 的<uri>元素和 taglib 指令的uri屬性,並找出相符合的
JSTL裡的 uri 看起來像個URL,但那只是為了讓標籤命名獨特,並不是需要輸入實際位置
範例中我們在 attr 屬性裡填入 "name" 作為值
但因為<rtexprvalue>設定是 true
所以其實也可以用EL、Sciptlet運算式或是<jsp:attribute>標準動作來輸入值
當然如此一來,屬性型別也是一個值得討論的問題
container預期EL的評算結果會符合標籤的setter方法的參數型別
如果不這樣撰寫的話會產生例外
倘若 <rtexprvalue> 為 false 或未定義,那就只能使用字串實字
標籤主體只有在 tld 的 <body-content> 不為empty時才能有主體
<body-content>裡可以宣告的值共有 empty、scriptless 、tagdependent、JSP
scriptless 會讓標籤不能有Scriptlet,但能有樣板文本、EL、自訂及標準動作
tagdependent會讓標籤主體作為純文字處理
設定成JSP則可以在主體中使用任何JSP裡的東西
所謂不能有主體的標籤有三種方式
第一是空標籤,例如 <xxx:xxx />
第二是在開始與結束標籤之間沒有任何東西,如 <xxx:xxx></xxx:xxx>
最後是開始與結束標籤之間只有<jsp:attribute>標準動作
因為<jsp:attribute>只是另一種設定標籤屬性的方法,故不算在主體內
程式碼如:
<xxx:xxx>
<jsp:attribute xxx="xxx">${test}</jsp:attribute>
</xxx:xxx>
接下來看另一個範例來做更深入的了解:
Iterator.tld=====
......
<tag>
<description></description>
<name>myTag2</name>
<tag-class>tw.vencs.DoTag</tag-class>
<body-content>scriptless</body-content>
<attribute>
<name>list</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
....
DoTag.java=====
package tw.vencs;
import javax.servlet.jsp.tagext.SimpleTagSupport;
import javax.servlet.jsp.JspException;
import java.io.IOException;
import java.util.Iterator;
import java.util.List;
public class DoTag extends SimpleTagSupport {
private List<String> list;
public void doTag() throws IOException, JspException{
Iterator<String> i = list.iterator();
while(i.hasNext()){
String str = (String)i.next();
getJspContext().setAttribute("item", str);
getJspBody().invoke(null);
}
}
public void setList(List<String> list){
this.list = list;
}
}
show.jsp========
...
<body>
<test:myTag2 list="${StrList }">
${item }<br />
</test:myTag2>
</body>
.....
getJspBody().invoke(null) 的意思是處理標籤的主體,並將它輸出到回應
null參數表示輸出會送往回應,而不是其他的 writer
範例中的標籤處理器會讀取儲存String類別的List後
以迴圈迭送的方式繞行,重新設定每一輪的"item"值後呼叫getJspBody().invoke(null)
陣列也可以用類似的方法寫成動態的輸出
標籤的運作有時需要依賴定的請求屬性
如果標籤找不到他需要的屬性,導致無法正常運作的情況下
也不能在這時候丟出 JspException,因為這樣會將整個頁面都清掉
SkipPageException 則是使用的好選擇
它能讓在標籤被呼叫之前就已經被評算的部分作為回應輸出
而例外發生後剩下的待處理部分會被丟棄
使用方法如下:
public void doTag() throws IOException, JspException{
.....
if( DontWork ){ //判斷式的部分無法運作的情形下丟出例外
throw new SkipPageException();
}
}
雖然還有一種自訂標籤的實作 - Classic Tag 沒有提到
但那是JSP2.0之前使用的東西了
現在大多使用繼承 SimpleTagSupport 的類別就能解決了
實作上幾乎用不到的就不提了
沒有留言:
張貼留言