一、JSP自定義標(biāo)簽簡介
標(biāo)簽是一種XML元素,通過標(biāo)簽可以使JSP網(wǎng)頁變得簡潔并且易于維護(hù),還可以方便地實(shí)現(xiàn)同一個(gè)JSP文件支持多種語言版本。由于標(biāo)簽是XML元素,所以它的名稱和屬性都是大小寫敏感的
標(biāo)準(zhǔn)JSP標(biāo)簽是用來調(diào)用JavaBean組件的操作,處理定向請求以簡化JSP頁面開發(fā)與維護(hù)。JSP技術(shù)提供了一種封裝其它動(dòng)態(tài)類型的機(jī)制——自定義標(biāo)簽,它擴(kuò)展了JSP語言。自定義標(biāo)簽通常發(fā)布在標(biāo)簽庫中,該庫定義了一個(gè)自定義標(biāo)簽集并包含實(shí)現(xiàn)標(biāo)簽的對象。
自定義標(biāo)簽是用戶定義的JSP語言元素。當(dāng)JSP頁面包含一個(gè)自定義標(biāo)簽時(shí)被轉(zhuǎn)化為servlet,標(biāo)簽轉(zhuǎn)化為對稱為tag handler的對象的操作。接著當(dāng)servlet執(zhí)行時(shí)Web container調(diào)用那些操作。
二、兩種標(biāo)簽
可以定義兩種類型的標(biāo)簽:
javax.servlet.jsp.tagext.Tag
javax.servlet.jsp.tagext.BodyTag
有標(biāo)簽體的標(biāo)簽必須實(shí)現(xiàn) BodyTag 接口。
<jsptag:map scope=“session” name=“tagMap”>
body
</jsptag:map>
也可能沒有標(biāo)簽體:
<jsptag:map/>
無標(biāo)簽體的簡單標(biāo)簽可以實(shí)現(xiàn) Tag 接口。
三、標(biāo)簽處理程序
int doStartTag() throws JspException---處理開始標(biāo)簽
int doEndTag() throws JspException---處理結(jié)束標(biāo)簽
Tag getParent()/void setParent(Tag t)---獲得/設(shè)置標(biāo)簽的父標(biāo)簽
void setPageContext(PageContext pc)--- pageContext 屬性的 setter 方法
void release() 釋放獲得的所有資源
doStartTag()和doEndTag()方法的返回值說明:
SKIP_BODY 表示不用處理標(biāo)簽體,直接調(diào)用doEndTag()方法。
SKIP_PAGE 忽略標(biāo)簽后面的jsp(SUN企業(yè)級應(yīng)用的首選)內(nèi)容。
EVAL_PAGE 處理標(biāo)簽后,繼續(xù)處理jsp(SUN企業(yè)級應(yīng)用的首選)后面的內(nèi)容。
EVAL_BODY_BUFFERED 表示需要處理標(biāo)簽體,且需要重新創(chuàng)建一個(gè)緩沖(調(diào)用setBodyContent方法)。
EVAL_BODY_INCLUDE 表示在現(xiàn)有的輸出流對象中處理標(biāo)簽體,但繞過setBodyContent()和doInitBody()方法
EVAL_BODY_AGAIN 對標(biāo)簽體循環(huán)處理。(存在于javax.servlet.jsp.tagext.IterationTag接口中)
實(shí)現(xiàn)javax.servlet.jsp.tagext.Tag接口
擴(kuò)展javax.servlet.jsp.tagext.TagSupport類
TagSupport 類定義了 get/setParent() 和 setPageContext(),這與所有標(biāo)簽處理程序幾乎相同。
get/setParent() 方法允許標(biāo)簽嵌套。
TagSupport 類還定義了一個(gè)可以被子類使用的 pageContext 實(shí)例變量 (protected PageContext pageContext),這個(gè)變量是由 setPageContext() 方法設(shè)置的。
在創(chuàng)建自定義標(biāo)簽之前,需要?jiǎng)?chuàng)建一個(gè) 標(biāo)簽處理程序。標(biāo)簽處理程序是一個(gè)執(zhí)行自定義標(biāo)簽操作的 Java 對象。在使用自定義標(biāo)簽時(shí),要導(dǎo)入一個(gè) 標(biāo)簽庫 —— 即一組標(biāo)簽/標(biāo)簽處理程序?qū)ΑMㄟ^在 Web 部署描述符中聲明庫導(dǎo)入它,然后用指令 taglib 將它導(dǎo)入 JSP 頁。
如果 JSP 容器在轉(zhuǎn)換時(shí)遇到了自定義標(biāo)簽,那么它就檢查 標(biāo)簽庫描述符(tag library descriptor) (TLD) 文件以查詢相應(yīng)的標(biāo)簽處理程序。TLD 文件對于自定義標(biāo)簽處理程序,就像 Web 部署描述符對于 servlet 一樣。
在運(yùn)行時(shí),JSP 頁生成的 servlet 得到對應(yīng)于這一頁面所使用的標(biāo)簽的標(biāo)簽處理程序的一個(gè)實(shí)例。生成的 servlet 用傳遞給它的屬性初始化標(biāo)簽處理程序。
標(biāo)簽處理程序?qū)崿F(xiàn)了 生存周期 方法。生成的 servlet 用這些方法通知標(biāo)簽處理程序應(yīng)當(dāng)啟動(dòng)、停止或者重復(fù)自定義標(biāo)簽操作。生成的 servlet 調(diào)用這些生存周期方法執(zhí)行標(biāo)簽的功能。
四、TLD 文件
TLD 文件的根元素是 taglib。taglib 描述了一個(gè) 標(biāo)簽庫 —— 即一組標(biāo)簽/標(biāo)簽處理程序?qū)Α?
因?yàn)槲覀兪褂玫氖?JSP 版本 1.2,所以在這個(gè)例子中需要 tlib-version 和 short-name 元素。
tlib-version 元素對應(yīng)于標(biāo)簽庫版本。
jsp-version 對應(yīng)于標(biāo)簽庫所依賴的 JSP 技術(shù)的版本。
short-name 元素定義了 IDE 和其他開發(fā)工具可以使用的標(biāo)簽庫的簡單名。
taglib 元素包含許多 tag 元素,標(biāo)簽庫中每一個(gè)標(biāo)簽有一個(gè) tag 元素。
在JSP中導(dǎo)入TLD文件:
<%@ taglib uri="firstTag" prefix="my"%>
五、編寫自定義迭代標(biāo)簽和el表達(dá)式調(diào)用類的靜態(tài)方法實(shí)例
循環(huán)標(biāo)簽體類:ForEach.java
1
import java.util.Collection;
2
import java.util.Iterator;
3
4
import javax.servlet.jsp.JspException;
5
import javax.servlet.jsp.tagext.BodyContent;
6
import javax.servlet.jsp.tagext.BodyTagSupport;
7
8
public class ForEach extends BodyTagSupport
9

{
10
private String id;
11
private String collection;
12
private Iterator iter;
13
14
public void setCollection(String collection)
15
{
16
this.collection = collection;
17
}
18
public void setId(String id)
19
{
20
this.id = id;
21
}
22
23
//遇到開始標(biāo)簽執(zhí)行
24
public int doStartTag() throws JspException
25
{
26
Collection coll = (Collection) pageContext.findAttribute(collection);
27
// 表示如果未找到指定集合,則不用處理標(biāo)簽體,直接調(diào)用doEndTag()方法。
28
if(coll==null||coll.isEmpty()) return SKIP_BODY;
29
30
iter = coll.iterator();
31
pageContext.setAttribute(id, iter.next());
32
// 表示在現(xiàn)有的輸出流對象中處理標(biāo)簽體,但繞過setBodyContent()和doInitBody()方法
33
// 這里一定要返回EVAL_BODY_INCLUDE,否則標(biāo)簽體的內(nèi)容不會在網(wǎng)頁上輸出顯示
34
return EVAL_BODY_INCLUDE;
35
}
36
37
//在doInitBody方法之前執(zhí)行,在這里被繞過不執(zhí)行
38
@Override
39
public void setBodyContent(BodyContent arg0)
40
{
41
System.out.println("setBodyContent
");
42
super.setBodyContent(arg0);
43
}
44
//此方法被繞過不會被執(zhí)行
45
@Override
46
public void doInitBody() throws JspException
47
{
48
System.out.println("doInitBody
");
49
super.doInitBody();
50
}
51
52
//遇到標(biāo)簽體執(zhí)行
53
public int doAfterBody() throws JspException
54
{
55
if(iter.hasNext())
56
{
57
pageContext.setAttribute(id, iter.next());
58
return EVAL_BODY_AGAIN;// 如果集合中還有對像,則循環(huán)執(zhí)行標(biāo)簽體
59
}
60
return SKIP_BODY;//迭代完集合后,跳過標(biāo)簽體,調(diào)用doEndTag()方法。
61
}
62
63
//遇到結(jié)束標(biāo)簽執(zhí)行
64
public int doEndTag() throws JspException
65
{
66
System.out.println("doEndTag
");
67
return EVAL_PAGE;
68
}
69
70
}
獲取VO屬性類:GetProperty.java
1
import java.lang.reflect.Method;
2
3
import javax.servlet.jsp.JspException;
4
import javax.servlet.jsp.tagext.BodyTagSupport;
5
6
public class GetProperty extends BodyTagSupport
7

{
8
9
private String name;
10
private String property;
11
12
public void setName(String name)
13
{
14
this.name = name;
15
}
16
17
public void setProperty(String property)
18
{
19
this.property = property;
20
}
21
22
@SuppressWarnings("unchecked")
23
public int doStartTag() throws JspException
24
{
25
try
26
{
27
Object obj = pageContext.findAttribute(name);
28
29
if (obj == null) return SKIP_BODY;
30
31
Class c = obj.getClass();
32
//構(gòu)造GET方法名字 get+屬性名(屬性名第一個(gè)字母大寫)
33
String getMethodName = "get" + property.substring(0, 1).toUpperCase()
34
+ property.substring(1, property.length());
35
Method getMethod = c.getMethod(getMethodName, new Class[]
{});
36
37
pageContext.getOut().print(getMethod.invoke(obj));
38
System.out.print(property + ":" + getMethod.invoke(obj) + "\t");
39
} catch (Exception e)
40
{
41
e.printStackTrace();
42
}
43
return SKIP_BODY;
44
}
45
46
public int doEndTag() throws JspException
47
{
48
return EVAL_PAGE;
49
}
50
}
51
52
表達(dá)式直接訪問此類中靜態(tài)的方法:ELFunction.java
53
public class ELFunction
54

{
55
public static int add( int i,int j )
56
{
57
return i+j;
58
}
59
}
寫一個(gè)測試用的VO類:UserVo.java
1
public class UserVo
2

{
3
private String name;
4
private String password;
5
6
public String getName()
7
{
8
return name;
9
}
10
public void setName(String name)
11
{
12
this.name = name;
13
}
14
public String getPassword()
15
{
16
return password;
17
}
18
public void setPassword(String password)
19
{
20
this.password = password;
21
}
22
}
建好TLD文件tag.tld,放在WEB-INF目錄下
1
<?xml version="1.0" encoding="utf-8"?>
2
<taglib version="2.0"
3
xmlns="http://java.sun.com/xml/ns/j2ee"
4
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5
xmlns:shcemalocation="http://java.sun.com/xml/ns/j2ee
6
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
7
8
<description>自定義標(biāo)簽</description>
9
<display-name>JSTL core</display-name>
10
<tlib-version>1.1</tlib-version>
11
<short-name>firstLabel</short-name>
12
<uri>http://java.sun.com/jsp/jstl/core</uri>
13
14
<!-- 創(chuàng)建自定義 迭代標(biāo)簽 -->
15
<tag>
16
<name>forEach</name>
17
<tag-class>exercise.taglib.ForEach</tag-class>
18
<!-- 如果沒有標(biāo)簽體,設(shè)置empty , 如果有標(biāo)簽休必須設(shè)置JSP-->
19
<body-content>JSP</body-content>
20
<attribute>
21
<name>id</name>
22
<required>true</required><!-- 標(biāo)識屬性是否是必須的 -->
23
<rtexprvalue>true</rtexprvalue><!-- 標(biāo)識屬性值是否可以用表達(dá)式語言 -->
24
</attribute>
25
<attribute>
26
<name>collection</name>
27
<required>true</required>
28
<rtexprvalue>true</rtexprvalue>
29
</attribute>
30
</tag>
31
32
<!-- 創(chuàng)建自定義獲得屬性標(biāo)簽 -->
33
<tag>
34
<name>getProperty</name>
35
<tag-class>exercise.taglib.GetProperty</tag-class>
36
<body-content>empty</body-content>
37
<attribute>
38
<name>name</name>
39
<required>true</required>
40
<rtexprvalue>true</rtexprvalue>
41
</attribute>
42
<attribute>
43
<name>property</name>
44
<required>true</required>
45
<rtexprvalue>true</rtexprvalue>
46
</attribute>
47
</tag>
48
49
<!-- 配置一個(gè)表達(dá)式調(diào)用 的函數(shù) -->
50
<function>
51
<name>add</name><!-- 配置一個(gè)標(biāo)簽,在JSP頁面通過引用前綴調(diào)用 -->
52
<function-class>exercise.taglib.ELFunction</function-class><!-- 實(shí)現(xiàn)類 -->
53
<function-signature>int add(int,int)</function-signature><!-- 靜態(tài)的方法:包括返回類型,方法名,入?yún)⒌念愋?nbsp;-->
54
</function>
55
</taglib>
在web.xml文件中配置自定義標(biāo)簽
1
<jsp-config>
2
<taglib>
3
<taglib-uri>firstTag</taglib-uri>
4
<taglib-location>/WEB-INF/tag.tld</taglib-location>
5
</taglib>
6
</jsp-config>
在jsp文件中使用標(biāo)簽:tag.jsp
1
<%
@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
2
<%
@ taglib uri="firstTag" prefix="my"%>
3
4
<jsp:useBean id="userVo1" class="exercise.vo.UserVo" scope="request">
5
<jsp:setProperty name="userVo1" property="name" value="Hackiller"/>
6
<jsp:setProperty name="userVo1" property="password" value="123"/>
7
</jsp:useBean>
8
9
<jsp:useBean id="userVo2" class="exercise.vo.UserVo" scope="request">
10
<jsp:setProperty name="userVo2" property="name" value="YangYang"/>
11
<jsp:setProperty name="userVo2" property="password" value="456"/>
12
</jsp:useBean>
13
14
<%
15
List list = new ArrayList();
16
list.add(userVo1);
17
list.add(userVo2);
18
pageContext.setAttribute("voList",list);
19
%>
20
21
<html>
22
<head>
23
<title>My JSP 'tag.jsp' starting page</title>
24
</head>
25
26
<body>
27
<h2 align="center">This is my JSP page:測試taglib.</h2>
28
<hr>
29
30
<h2>自定義迭代標(biāo)簽:</h2>
31
<table>
32
<tr><td>姓名</td><td>密碼</td></tr>
33
<my:forEach collection="voList" id="uservo">
34
<tr>
35
<td><my:getProperty name="uservo" property="name"/></td>
36
<td><my:getProperty name="uservo" property="password"/></td>
37
</tr>
38
</my:forEach>
39
</table>
40
<hr>
41
42
<h2>表達(dá)式調(diào)用類的靜態(tài)方法:</h2>
43
2+5=${my:add(2,5)}
44
</body>
45
</html>