一、自定義标簽入門之無參數自定義标簽
1.開發自定義标簽類
當我們在JSP頁面使用一個簡單的标簽時,底層實際上由标簽處理類提供支援,進而可以使用簡單的标簽來封裝複雜的功能,進而使團隊更好地協作開發(能讓美勞工員更好地參與JSP頁面的開發)。
自定義标簽類都必須繼承一個父類:javax.servlet.jsp.tagext.SimpleTagSupport,或者TagSupport除此之外,JSP自定義标簽類還有如下要求。
如果标簽類包含屬性,每個屬性都有對應的getter和setter方法。
重寫doTag()或者doStartTag()或doEndTag()方法方法,這個方法負責生成頁面内容。
首先介紹是不帶屬性的标簽以HelloWorld為例:
Java代碼如下:
public class HelloWorldTag extends TagSupport {
private static final long serialVersionUID = -3382691015235241708L;
@Override
public int doEndTag() throws JspException {
try {
pageContext.getOut().write("Hello World !");
return super.doEndTag();
} catch (JspException e) {
e.printStackTrace();
return 0;
} catch (IOException e) {
e.printStackTrace();
return 0;
}
}
@Override
public int doStartTag() {
try {
pageContext.getOut().write("Hello World");
return super.doStartTag();
} catch (JspException e) {
e.printStackTrace();
return 0;
} catch (IOException e) {
e.printStackTrace();
return 0;
}
}
}
注意:
問題1:tagsupport中的dostartTag和doEndTag這兩個方法有什麼差別
doStartTag是在掃描到起始标簽時調用,doEndTag是在掃描到結束标簽是調用。
例如:<helloWorld> helloWorld</helloWorld>
hxyy2013.b2b168.com
http://www.wenbing.com/kmhx
則jsp引擎分析到<helloWorld> 時調用doStratTag, 分析到</helloWorld>時調用doEndTag
2、建立TLD檔案
TLD是Tag Library Definition的縮寫,即标簽庫定義,檔案的字尾是tld,每個TLD檔案對應一個标簽庫,一個标簽庫中可包含多個标簽,TLD檔案也稱為标簽庫定義檔案。
标簽庫定義檔案的根元素是taglib,它可以包含多個tag子元素,每個tag子元素都定義一個标簽。通常我們可以到Web容器下複制一個标簽庫定義檔案,并在此基礎上進行修改即可。例如Tomcat6.0,在webapps\examples\WEB-INF\jsp2路徑下包含了一個jsp2-example-taglib.tld檔案,這就是示範用的标簽庫定義檔案。
将該檔案複制到Web應用的WEB-INF/路徑,或WEB-INF的任意子路徑下,并對該檔案進行簡單修改,修改後的helloworld.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 web-jsptaglibrary_2_0.xsd"
version="2.0">
<tlib-version>1.0</tlib-version>
<short-name>myhelloworld</short-name>
<!-- 定義該标簽庫的URI 必須添加但可以空-->
<uri></uri>
<!-- 定義第一個标簽 -->
<tag>
<!-- 定義标簽名 -->
<name>helloWorld</name>
<!-- 定義标簽處理類 -->
<tag-class>org.lxh.taglib.HelloWorldTag</tag-class>
<!-- 定義标簽體為空 -->
<body-content>empty</body-content>
</tag>
</taglib>
問題1: 為什麼要用TagSupport與BodyTagSupport的差別主要是标簽處理類是否需要與标簽體互動,如果不需要互動的就用TagSupport,否則就用BodyTagSupport。
互動就是标簽處理類是否要讀取标簽體的内容和改變标簽體傳回的内容。用TagSupport實作的标簽,都可以用BodyTagSupport來實作,因為BodyTagSupport繼承了TagSupport而不去實作IterationTag接口的,因為BodyTagSupport繼承了TagSupport類,并且該類已經實作了IterationTag接口并且實作了功能.
doStartTag()方法在标簽開始時執行,要記住每次都要對類進行初始化,避免上一次的遺留資料對操作造成影響。然後判斷是否有資料需要處理,如果有,則傳回EVAL_BODY_INCLUDE開始處理标簽裡的内容,如果沒有,傳回 EVAL_PAGE跳過标簽内容執行标簽下面的内容。
doAfterBody()方法在每次處理完标簽内部内容後執行,判斷循環是否已經結束,如果可以繼續循環,傳回EVAL_BODY_AGAIN用循環得到新的資料再次處理标簽内部内容,如果循環結束就傳回EVAL_PAGE結束标簽。
二、自定義JSP标簽的處理過程:
1.在JSP中引入标簽庫:
2.在JSP中使用标簽庫标簽
3.Web容器根據第二個步驟中的prefix,獲得第一個步驟中聲明的taglib的uri屬性值
4.Web容器根據uri屬性在web.xml找到對應的元素
5.從元素中獲得對應的元素的值
6.Web容器根據元素的值從WEB-INF/目錄下找到對應的.tld檔案
7.從.tld檔案中找到與tagname對應的元素
8.湊元素中獲得對應的元素的值
9.Web容器根據元素的值建立相應的tag handle class的執行個體
10. Web容器調用這個執行個體的doStartTag/doEndTag方法完成相應的處理
三、建立和使用一個Tag Library的基本步驟:
1.建立标簽的處理類(Tag Handler Class)
2.建立标簽庫描述檔案(Tag Library Descrptor File)
3.在web.xml檔案中配置元素
4.在JSP檔案中引人标簽庫
四、TagSupport類簡介:
1.處理标簽的類必須擴充javax.servlet.jsp.TagSupport.
2.TagSupport類的主要屬性:
A.parent屬性:代表嵌套了目前标簽的上層标簽的處理類
B.pageContex屬性:代表Web應用中的javax.servlet.jsp.PageContext對象
3.JSP容器在調用doStartTag或者doEndTag方法前,會先調用setPageContext和setParent方法,設定pageContext和parent。是以在标簽處理類中可以直接通路pageContext變量
4.在TagSupport的構造方法中不能通路pageContext成員變量,因為此時JSP容器還沒有調用setPageContext方法對pageContext進行初始化
五、TagSupport處理标簽的方法:
1.TagSupport類提供了兩個處理标簽的方法:
public int doStartTag() throws JspException
public int doEndTag() throws JspException
2.doStartTag:但JSP容器遇到自定義标簽的起始标志,就會調用doStartTag()方法,doStartTag()方法傳回一個整數值,用來決定程式的後續流程。
A.Tag.SKIP_BODY:表示跳過了開始和結束标簽之間的代碼
B.Tag.EVAL_BODY_INCLUDE:表示标簽之間的内容被正常執行
C.Tag.EVAL_BODY_BUFFERED :對包含的内容進行解析
3.doEndTag:但JSP容器遇到自定義标簽的結束标志,就會調用doEndTag()方法。doEndTag()方法也傳回一個整數值,用來決定程式後續流程。
A.Tag.SKIP_PAGE:表示立刻停止執行網頁,網頁上未處理的靜态内容和JSP程式均被忽略任何已有的輸出内容立刻傳回到客戶的浏覽器上。
B.Tag.EVAL_PAGE:表示按照正常的流程繼續執行JSP網頁
4.doAfterTag:遇到标簽體執行
A.Tag.EVAL_BODY_AGAIN;// 如果集合中還有對像,則循環執行标簽體,對标簽體循環處理,(存在于javax.servlet.jsp.tagext.IterationTag接口中)
B.Tag.SKIP_BODY
六、建立含有字段的标簽:
1.建立标簽處理器類FieldTag
package com.able.tag;
import java.io.IOException;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.TagSupport;
public class FieldTag extends TagSupport {
private static final long serialVersionUID = 1540529069962423355L;
private String field;
private Integer count;
@Override
public int doEndTag() throws JspException {
try {
JspWriter out = pageContext.getOut();
out.print(field);
out.print(count);
} catch (IOException e) {
e.printStackTrace();
}
return super.doEndTag();
}
public String getField() {
return field;
}
public void setField(String field) {
this.field = field;
}
public Integer getCount() {
return count;
}
public void setCount(Integer count) {
this.count = count;
}
}
2.在tag.tld檔案中天劍tag标簽
<tag>
<!-- 定義标簽名 -->
<name>field</name>
<!-- 定義标簽處理類 -->
<tag-class>com.able.tag.FieldTag</tag-class>
<!-- 定義标簽體為空 -->
<body-content>empty</body-content>
<attribute>
<name>field</name>
<required>true</required> <!-- 是否必須指派 -->
<rtexprvalue>true</rtexprvalue><!-- 表示是否接受jsp文法或者el語言或其他動态語言,預設false -->
</attribute>
<attribute>
<name>count</name>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
3.jsp中定義标簽:
<tm:field field="11" count="22"/>
七、如何建立标簽處理類
1、引入必需的資源
import javax.servlet.jsp.*; import javax.servlet.http.*; import java.util.*; import java.io.*;
2、繼承TagSupport類并覆寫doStartTag()/doEndTag()方法
3、從ServletContext對象中擷取java.util.Properties對象
4、從Properties對象中擷取key對應的屬性值
5、對擷取的屬性進行相應的處理并輸出結果
建立标簽庫描述檔案(Tag Library Descriptor)
1、标簽庫描述檔案,簡稱TLD,采用XML檔案格式,定義了使用者的标簽庫。TLD檔案中的元素可以分成3類:
A.标簽庫元素
B.标簽元素
C.标簽屬性元素
2、标簽庫元素用來設定标簽庫的相關資訊,它的常用屬性有:
A.shortname:指定Tag Library預設的字首名(prefix);
B.uri:設定Tag Library的惟一通路表示符。
3、标簽元素用來定義一個标簽,它的常見屬性有:
A.name:設定Tag的名字;
B.tagclass:設定Tag的處理類;
C.bodycontent:設定标簽的主體(body)内容。
1)empty:表示标簽中沒有body;
2)JSP:表示标簽的body中可以加入JSP程式代碼;
3)tagdependent:表示标簽中的内容由标簽自己去處理。
4、标簽屬性元素用來定義标簽的屬性,它的常見屬性有:
A.name:屬性名稱;
B.required:屬性是否必需的,預設為false;
C.rtexprvalue:屬性值是否可以為request-time表達式,也就是類似于< %=…% >的表達式。
八、在Web應用中使用标簽
1、如果Web應用中用到了自定義JSP标簽,則必須在web.xml檔案中加入元素,它用于聲明所引用的标簽所在的标簽庫
/sometaglib
/WEB-INF/someTLD.tld
2、設定Tag Library的惟一标示符,在Web應用中将根據它來引用Tag Libray;
3、指定和Tag Library對應的TLD檔案的位置;
4、在JSP檔案中需要加入<!-- taglib% >指令來聲明對标簽庫的引用。
5、prefix表示在JSP網頁中引用這個标簽庫的标簽時的字首,uri用來指定Tag Library的辨別符,它必須和web.xml中的屬性保持一緻。
九、案例:
4.1.建立标簽描述符檔案
在WEB-INF檔案下建立*.tld标簽描述符檔案:如
<taglib>
<tlibversion>1.0</tlibversion>
<jspversion>1.1</jspversion>
<shortname>eRedLab JSPTag Library</shortname>
<uri>/testTag</uri>
<info>自定義标簽測試</info>
<tag>
<name>hello</name>
<tagclass>com.eredlab.taglib.test.TestTld</tagclass>
<bodycontent>empty</bodycontent>
<info>自定義标簽測試</info>
<attribute>
<name>begin</name>
<required>true</required>
</attribute>
<name>end</name>
</tag>
</taglib>
4.2.建立标簽處理器
/**
* @desc 自定義标簽測試類 實作一個簡單的Hello World标簽
* @author 夏中偉
* @version eRedLab 2007-9-10
*/
public class TestTld extends TagSupport{
//标簽屬性begin
private String begin = null;
//标簽屬性end
private String end = null;
//構造函數
public TestTld(){
}
/* 标簽初始方法 */
public int doStartTag() throws JspTagException{
return super.EVAL_BODY_INCLUDE;
/* 标簽結束方法 */
public int doEndTag() throws JspTagException{
JspWriter out = pageContext.getOut();
String sum = begin + end;
try{
//标簽的傳回值
out.println(sum);
}catch(IOException e){
e.printStackTrace();
}
return super.SKIP_BODY;
/* 釋放資源 */
public void release(){
super.release();
/********************************************
屬性get()、set()方法
*******************************************/
在Web.XML中加載标簽描述符檔案.
<!-- 加載标簽描述符檔案 -->
<taglib-uri>/WEB-INF/test.tld</taglib-uri>
<taglib-location>/WEB-INF/test.tld</taglib-location>
5.2.在JSP中使用此标簽
<%@ taglib uri="/testTag" prefix="mytag"%>
<mytag:hello end="夏中偉!" begin="自定義标簽輸出流:Hello,"/>
<mytag:hello end="World!" begin="Hi,"/>
WEB頁面輸出結果如下:
自定義标簽輸出流:Hello,夏中偉! Hi,World!
循環标簽體類:ForEach.java
1import java.util.Collection;
2import java.util.Iterator;
3
4import javax.servlet.jsp.JspException;
5import javax.servlet.jsp.tagext.BodyContent;
6import javax.servlet.jsp.tagext.BodyTagSupport;
7
8public 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 //遇到開始标簽執行
24 public int doStartTag() throws JspException
25 {
26 Collection coll = (Collection) pageContext.findAttribute(collection);
27 // 表示如果未找到指定集合,則不用處理标簽體,直接調用doEndTag()方法。
28 if(coll==null||coll.isEmpty()) return SKIP_BODY;
29
30 iter = coll.iterator();
31 pageContext.setAttribute(id, iter.next());
32 // 表示在現有的輸出流對象中處理标簽體,但繞過setBodyContent()和doInitBody()方法
33 // 這裡一定要傳回EVAL_BODY_INCLUDE,否則标簽體的内容不會在網頁上輸出顯示
34 return EVAL_BODY_INCLUDE;
35 }
36
37 //在doInitBody方法之前執行,在這裡被繞過不執行
38 @Override
39 public void setBodyContent(BodyContent arg0)
40 {
41 System.out.println("setBodyContent");
42 super.setBodyContent(arg0);
43 }
44 //此方法被繞過不會被執行
45 @Override
46 public void doInitBody() throws JspException
47 {
48 System.out.println("doInitBody");
49 super.doInitBody();
50 }
51
52 //遇到标簽體執行
53 public int doAfterBody() throws JspException
54 {
55 if(iter.hasNext())
56 {
57 pageContext.setAttribute(id, iter.next());
58 return EVAL_BODY_AGAIN;// 如果集合中還有對像,則循環執行标簽體
59 }
60 return SKIP_BODY;//疊代完集合後,跳過标簽體,調用doEndTag()方法。
61 }
62
63 //遇到結束标簽執行
64 public int doEndTag() throws JspException
65 {
66 System.out.println("doEndTag");
67 return EVAL_PAGE;
68 }
69
70}
擷取VO屬性類:GetProperty.java
1import java.lang.reflect.Method;
2
3import javax.servlet.jsp.JspException;
4import javax.servlet.jsp.tagext.BodyTagSupport;
5
6public 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 //構造GET方法名字 get+屬性名(屬性名第一個字母大寫)
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表達式直接通路此類中靜态的方法:ELFunction.java
53public class ELFunction
54{
55 public static int add( int i,int j )
56 {
57 return i+j;
58 }
59}
寫一個測試用的VO類:UserVo.java
1public 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>自定義标簽</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 <!-- 建立自定義 疊代标簽 -->
15 <tag>
16 <name>forEach</name>
17 <tag-class>exercise.taglib.ForEach</tag-class>
18 <!-- 如果沒有标簽體,設定empty , 如果有标簽休必須設定JSP-->
19 <body-content>JSP</body-content>
20 <attribute>
21 <name>id</name>
22 <required>true</required><!-- 辨別屬性是否是必須的 -->
23 <rtexprvalue>true</rtexprvalue><!-- 辨別屬性值是否可以用表達式語言 -->
24 </attribute>
25 <attribute>
26 <name>collection</name>
27 <required>true</required>
28 <rtexprvalue>true</rtexprvalue>
29 </attribute>
30 </tag>
31
32 <!-- 建立自定義獲得屬性标簽 -->
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 <!-- 配置一個表達式調用 的函數 -->
50 <function>
51 <name>add</name><!-- 配置一個标簽,在JSP頁面通過引用字首調用 -->
52 <function-class>exercise.taglib.ELFunction</function-class><!-- 實作類 -->
53 <function-signature>int add(int,int)</function-signature><!-- 靜态的方法:包括傳回類型,方法名,入參的類型 -->
54 </function>
55</taglib>
<jsp-config>
<taglib>
<taglib-uri>firstTag</taglib-uri>
<taglib-location>/WEB-INF/tag.tld</taglib-location>
</taglib>
</jsp-config>
在jsp檔案中使用标簽:tag.jsp
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
<%@ taglib uri="firstTag" prefix="my"%>
<jsp:useBean id="userVo1" scope="request">
<jsp:setProperty name="userVo1" property="name" value="Hackiller"/>
<jsp:setProperty name="userVo1" property="password" value="123"/>
</jsp:useBean>
<jsp:useBean id="userVo2" scope="request">
<jsp:setProperty name="userVo2" property="name" value="YangYang"/>
<jsp:setProperty name="userVo2" property="password" value="456"/>
</jsp:useBean>
<%
List list = new ArrayList();
list.add(userVo1);
list.add(userVo2);
pageContext.setAttribute("voList",list);
%>
<html>
<head>
<title>My JSP 'tag.jsp' starting page</title>
</head>
<body>
<h2 align="center">This is my JSP page:測試taglib.</h2>
<hr>
<h2>自定義疊代标簽:</h2>
<table>
<tr><td>姓名</td><td>密碼</td></tr>
<my:forEach collection="voList" id="uservo">
<tr>
<td><my:getProperty name="uservo" property="name"/></td>
<td><my:getProperty name="uservo" property="password"/></td>
</tr>
</my:forEach>
</table>
<hr>
<h2>表達式調用類的靜态方法:</h2>
2+5=${my:add(2,5)}
</body>
</html>