自定義標簽是用戶定義的JSP語言元素。當JSP頁面包含一個自定義標簽時將被轉(zhuǎn)化為servlet,標簽轉(zhuǎn)化為對被 稱為tag handler的對象的操作,即當servlet執(zhí)行時Web container調(diào)用那些操作。
JSP標簽擴展可以讓你創(chuàng)建新的標簽并且可以直接插入到一個JSP頁面。 JSP 2.0規(guī)范中引入Simple Tag Handlers來編寫這些自定義標記。
你可以繼承SimpleTagSupport類并重寫的doTag()方法來開發(fā)一個最簡單的自定義標簽。
創(chuàng)建"Hello"標簽
接下來,我們想創(chuàng)建一個自定義標簽叫作<ex:Hello>,標簽格式為:
<ex:Hello />
要創(chuàng)建自定義的JSP標簽,你首先必須創(chuàng)建處理標簽的Java類。所以,讓我們創(chuàng)建一個HelloTag類,如下所示:
package com.runoob;
import javax.servlet.jsp.tagext.*;
import javax.servlet.jsp.*;
import java.io.*;
public class HelloTag extends SimpleTagSupport {
public void doTag() throws JspException, IOException {
JspWriter out = getJspContext().getOut();
out.println("Hello Custom Tag!");
}
}
以下代碼重寫了doTag()方法,方法中使用了getJspContext()方法來獲取當前的JspContext對象,并將"Hello Custom Tag!"傳遞給JspWriter對象。
編譯以上類,并將其復制到環(huán)境變量CLASSPATH目錄中。最后創(chuàng)建如下標簽庫:<Tomcat安裝目錄>webapps\ROOT\WEB-INF\custom.tld。
<taglib>
<tlib-version>1.0</tlib-version>
<jsp-version>2.0</jsp-version>
<short-name>Example TLD</short-name>
<tag>
<name>Hello</name>
<tag-class>com.runoob.HelloTag</tag-class>
<body-content>empty</body-content>
</tag>
</taglib>
接下來,我們就可以在JSP文件中使用Hello標簽:
<%@ taglib prefix="ex" uri="WEB-INF/custom.tld"%>
<html>
<head>
<title>A sample custom tag</title>
</head>
<body>
<ex:Hello/>
</body>
</html>
以上程序輸出結(jié)果為:
Hello Custom Tag!
訪問標簽體
你可以像標準標簽庫一樣在標簽中包含消息內(nèi)容。如我們要在我們自定義的Hello中包含內(nèi)容,格式如下:
<ex:Hello>
This is message body
</ex:Hello>
我們可以修改標簽處理類文件,代碼如下:
package com.runoob;
import javax.servlet.jsp.tagext.*;
import javax.servlet.jsp.*;
import java.io.*;
public class HelloTag extends SimpleTagSupport {
StringWriter sw = new StringWriter();
public void doTag()
throws JspException, IOException
{
getJspBody().invoke(sw);
getJspContext().getOut().println(sw.toString());
}
}
接下來我們需要修改TLD文件,如下所示:
<taglib>
<tlib-version>1.0</tlib-version>
<jsp-version>2.0</jsp-version>
<short-name>Example TLD with Body</short-name>
<tag>
<name>Hello</name>
<tag-class>com.runoob.HelloTag</tag-class>
<body-content>scriptless</body-content>
</tag>
</taglib>
現(xiàn)在我們可以在JSP使用修改后的標簽,如下所示:
<%@ taglib prefix="ex" uri="WEB-INF/custom.tld"%>
<html>
<head>
<title>A sample custom tag</title>
</head>
<body>
<ex:Hello>
This is message body
</ex:Hello>
</body>
</html>
以上程序輸出結(jié)果如下所示:
This is message body
自定義標簽屬性
你可以在自定義標準中設置各種屬性,要接收屬性,值自定義標簽類必須實現(xiàn)setter方法, JavaBean 中的setter方法如下所示:
package com.runoob;
import javax.servlet.jsp.tagext.*;
import javax.servlet.jsp.*;
import java.io.*;
public class HelloTag extends SimpleTagSupport {
private String message;
public void setMessage(String msg) {
this.message = msg;
}
StringWriter sw = new StringWriter();
public void doTag()
throws JspException, IOException
{
if (message != null) {
/* 從屬性中使用消息 */
JspWriter out = getJspContext().getOut();
out.println( message );
}
else {
/* 從內(nèi)容體中使用消息 */
getJspBody().invoke(sw);
getJspContext().getOut().println(sw.toString());
}
}
}
屬性的名稱是"message",所以setter方法是的setMessage()。現(xiàn)在讓我們在TLD文件中使用的<attribute>元素添加此屬性:
<taglib>
<tlib-version>1.0</tlib-version>
<jsp-version>2.0</jsp-version>
<short-name>Example TLD with Body</short-name>
<tag>
<name>Hello</name>
<tag-class>com.runoob.HelloTag</tag-class>
<body-content>scriptless</body-content>
<attribute>
<name>message</name>
</attribute>
</tag>
</taglib>
現(xiàn)在我們就可以在JSP文件中使用message屬性了,如下所示:
<%@ taglib prefix="ex" uri="WEB-INF/custom.tld"%>
<html>
<head>
<title>A sample custom tag</title>
</head>
<body>
<ex:Hello message="This is custom tag" />
</body>
</html>
以上實例數(shù)據(jù)輸出結(jié)果為:
This is custom tag
你還可以包含以下屬性:
屬性 | 描述 |
---|---|
name | 定義屬性的名稱。每個標簽的是屬性名稱必須是唯一的。 |
required | 指定屬性是否是必須的或者可選的,如果設置為false為可選。 |
rtexprvalue | 聲明在運行表達式時,標簽屬性是否有效。 |
type | 定義該屬性的Java類類型 。默認指定為 String |
description | 描述信息 |
fragment | 如果聲明了該屬性,屬性值將被視為一個 JspFragment。 |
以下是指定相關的屬性實例:
.....
<attribute>
<name>attribute_name</name>
<required>false</required>
<type>java.util.Date</type>
<fragment>false</fragment>
</attribute>
.....
如果你使用了兩個屬性,修改TLD文件,如下所示:
.....
<attribute>
<name>attribute_name1</name>
<required>false</required>
<type>java.util.Boolean</type>
<fragment>false</fragment>
</attribute>
<attribute>
<name>attribute_name2</name>
<required>true</required>
<type>java.util.Date</type>
</attribute>
.....
如您還有不明白的可以在下面與我留言或是與我探討QQ群308855039,我們一起飛!
然JSTL提供了很多豐富的標簽,但是在某些情況下,這些標簽還是不能夠滿足我們的業(yè)務需求,所以sun公司也提供了自定義標簽的功能,可以根據(jù)實際業(yè)務需求創(chuàng)建相應的標簽。自定義標簽在一些很老的項目中會經(jīng)常見到,因為那個時候基本上是基于Servlet和JSP進行項目的開發(fā),為了應對自身的項目需求,每個公司都會自己創(chuàng)建自己的標簽庫,從而簡化開發(fā),提高開發(fā)效率。
自定義JSTL標簽只需要下面幾個步驟即可:
創(chuàng)建一個CustomTag類,繼承自SimpleTagSupport類,重寫doTag()方法。
package com.gitcode.tag;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.JspFragment;
import javax.servlet.jsp.tagext.SimpleTagSupport;
import java.io.IOException;
import java.io.StringWriter;
/**
* 自定義JSTL標簽
*/
public class CustomTag extends SimpleTagSupport {
/**
* 自定義標簽屬性
*/
private String content;
/**
* 標簽體內(nèi)容
*/
private StringWriter body = new StringWriter();
public void setContent(String content) {
this.content = content;
}
@Override
public void doTag() throws JspException, IOException {
// 1、獲取輸出流對象
JspWriter writer = this.getJspContext().getOut();
if (content == null) {
// 屬性等于空,則直接獲取標簽之間的body內(nèi)容
JspFragment jspBody = this.getJspBody();
// 調(diào)用方法
jspBody.invoke(body);
// 輸出內(nèi)容
writer.println(body.toString());
} else {
// 如果屬性名稱不為空,則輸出屬性名稱的內(nèi)容
writer.println(content);
}
}
}
在WEB-INF目錄下,我們創(chuàng)建一個tld目錄,用于保存我們自定義JSTL標簽的tld描述文件,tld描述文件中,需要編寫如下內(nèi)容:
<?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.1</tlib-version>
<short-name>custom_tag</short-name>
<tag>
<description>自定義JSTL標簽的描述內(nèi)容</description>
<name>custom_tag</name>
<tag-class>com.gitcode.tag.CustomTag</tag-class>
<!-- 標簽之間的內(nèi)容是腳本 -->
<body-content>scriptless</body-content>
<attribute>
<description>屬性的描述內(nèi)容</description>
<name>content</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
</taglib>
如下圖所示:
新建一個custom.jsp頁面,使用<%@taglib%>指令引入自定義標簽,如下所示:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%-- 引入標簽庫 --%>
<%@ taglib prefix="custom_tag" uri="WEB-INF/tld/custom_tag.tld" %>
<html>
<head>
<title>JSTL標簽庫之自定義標簽</title>
</head>
<body>
<h3>JSTL標簽庫之自定義標簽</h3>
<custom_tag:custom_tag content="content屬性內(nèi)容"></custom_tag:custom_tag>
<h3>JSTL標簽庫之自定義標簽</h3>
<custom_tag:custom_tag>
<ul>
<li>第一行</li>
<li>第二行</li>
<li>第三行</li>
</ul>
</custom_tag:custom_tag>
</body>
</html>
運行結(jié)果如下所示:
需要注意的是,prefix屬性值必須和custom_tag.tld描述文件中的<short-name>標簽值保持一致,否則無法引用自定義標簽。
以上,就是JSTL自定義標簽的使用,創(chuàng)建自定義標簽可以將公用的代碼封裝起來,后續(xù)要使用的時候,可以減少代碼的開發(fā),從而提高開發(fā)效率。
今天就到這里,未完待續(xù)~~
實現(xiàn)自定義標簽時,有時需要對標簽體的內(nèi)容進行處理以后再向瀏覽器輸出,比如將小寫英文字母轉(zhuǎn)化為大寫,將HTML標簽進行轉(zhuǎn)義等。為了實現(xiàn)這樣的功能,JSP規(guī)范中它義了一個BodyTag接口,它繼承自IterationTag接口,并在IterationTag接口基礎上新增了兩個方法和一個靜態(tài)常量,具體如下。
1. EVAL_BODY_BUFFERED常量
如果標簽處理器類實現(xiàn)了BodyTag接口,它的doStartTag()方法除了可以返回SKIP_BODY和EVAL_BODY_INCLUDE常量之外,還可以返回EVAL_BODY_BUFFERED常量。當doStartTag()方法返回EVAL_BODY_BUFFERED常量時,JSP容器將會創(chuàng)建個javax.servlet.jsp.tagext.BodyContent對象,使用該對象來執(zhí)行標簽體。關于BodyContent類的用法,將在下面進行詳細的講解。
2. setBodyContent(BodyContent b)方法
當且僅當doStartTag()方法返回EVAL_BODY_BUFFERED常量時,JSP容器才會調(diào)用setBodyContent()方法,通過該方法將BodyContent對象傳遞給標簽處理器類使用。
3. dolnitBody()方法
JSP容器在調(diào)用setBodyContent()方法后會調(diào)用doInitBody()方法來完成些初始化工作,該方法的調(diào)用在標簽體執(zhí)行之前。
其中,最重要的是setBodyContent()方法。為了幫助大家更好地理解BodyTag接口處理標簽內(nèi)容的方式,有必要對BodyContent類進行詳細講解。
BodyContent類是JspWriter類的子類,它在JspWriter的基礎上增加了一個用于存儲數(shù)據(jù)的緩沖區(qū)(確切地說緩沖區(qū)是在BodyContent的子類org.apache.jasper.runtime.BodyContentImple中定義的),當調(diào)用BodyContent對象的方法寫數(shù)據(jù)時,數(shù)據(jù)將被寫人到BodyContent內(nèi)部的緩沖區(qū)中。
明白了BodyContent類的這個特點,就不難理解JSP容器是如何利用BodyContent對象來處理標簽體內(nèi)容了。當標簽處理器類的doStartTag()方法返回EVAL_BODY_BUFFERED常量時,JSP容器會創(chuàng)建一個BodyContent對象,然后調(diào)用該對象的write()方法將標簽體的內(nèi)容寫人BodyContent對象的緩沖區(qū)中,開發(fā)者只要能夠訪問BodyContent緩沖區(qū)的內(nèi)容,就能對標簽體的內(nèi)容進行處理。在BodyContent類中定義了一些用于訪問緩沖區(qū)內(nèi)容的方法,具體如下表所示。
BodyContent類的常用方法 | |
方法聲明 | 功能描述 |
String getString() | 以字符串的形式返回BodyContent對象緩沖區(qū)中保存的數(shù)據(jù) |
Reader getReader() | 返回一個關聯(lián)BodyContent對象緩沖區(qū)中數(shù)據(jù)的Reader對 |
void clearBody() | 用于清空BodyContent對象緩沖區(qū)中的內(nèi)容 |
JspWriter getEnclosingWriter() | 用于返回BodyContent對象中關聯(lián)的JspWriter對象。當JSP容器創(chuàng)建BodyContent對象后,PageContext對象中的"out"屬性不再指向JSP的隱式對象,而是指向新創(chuàng)建的BodyContent對象。同時,在BodyContent對象中會用一個JspWriter類型的成員變量enclosingWriter記住原來的隱式對象,getEnclosingWriter()方 法返回的就是原始的JSP隱式對象 |
writerOut(Writer out) | 用于將BodyContent對象中的內(nèi)容寫人到指定的輸出流 |
在上表列舉的所有方法中,其中g(shù)etEnclosingWriter()方法最難理解,但是,只需要記住該方法的返回值為out即可。
除了BodyContent類外,在BodyTag接口還會涉及很多常量和方法,為了讓大家更好地掌握標簽處理器的執(zhí)行流程,接下來通過一張圖來描述,具體如下圖所示。
標簽處理器的執(zhí)行流程
上圖中清楚地描述了JSP容器執(zhí)行標簽處理器的過程。其中,release()方法之所以使用用虛線,是因為這個方法不會在標簽處理器每次執(zhí)行都被JSP容器調(diào)用,只有當標簽處理器對象作為垃圾被回收之前它才會被調(diào)用。傳統(tǒng)標簽的處理器是單例的,只會被創(chuàng)建和銷毀一次。
接下來,通過實現(xiàn)自定義標簽,學習如何使用BodyTag接口將標簽體中的小寫英文字母轉(zhuǎn)換為大寫,具體步驟如下。
(1)編寫標簽處理器類ToUpperCase.java。
JSP規(guī)范中定義了一個類BodyTagSupport實現(xiàn)了BodyTag接口,為了簡化程序的編寫,標簽處理器類ToUpperCase.java只需要繼承BodyTagSupport類即可。ToUpperCase.java類的實現(xiàn)代碼如下例所示。
package cn.itcast.chapter09.classisctag;
import java.io.IOException;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.BodyTagSupport;
public class ToUpperCase extends BodyTagSupport {
//定義doEndTag()方法
public int doEndTag() throws JspException {
//獲取級沖區(qū)中數(shù)據(jù)
String content = getBodyContent().getString();
//將數(shù)據(jù)轉(zhuǎn)為大寫
content = content.toUpperCase();
try{
//輸出數(shù)據(jù)內(nèi)容(兩種方式均可)
//pageContext.getout().write(content);
bodyContent.getEnclosingWriter().write(content);
} catch(IOException e){
e.printStackTrace();
return super.doEndTag();
}
}
由于BodyTagSupport類中的doStartTag()方法默認返回EVAL_BODY_BUFFERED常量,JSP容器會在執(zhí)行標簽體之前創(chuàng)建BodyContent對象,然后將標簽體內(nèi)容通過setBodyContent()方法設置給BodyContent對象。因此在上面案例中的doEndTag()方法中可以直接使用getBodyContent()方法的getString()方法獲得寫人到BodyContent緩沖區(qū)中的內(nèi)容,然后將其轉(zhuǎn)換為大寫,通過調(diào)用getEnclosingWriter()方法獲取到out對象,將內(nèi)容輸出到瀏覽器中。
注意:不能直接使用doStartTag()方法的原因是,執(zhí)行doStartTag()方法時,BodyContent對象中還沒有緩存標簽體的內(nèi)容,因此通過getBodyContent()方法還無法獲得標簽的內(nèi)容。
(2)注冊標簽處理器類。
在mytag.tld文件中增加一個Tag元素,對標簽處理器類進行注冊,注冊信息如下所示。
<tag>
<name>toUpperCase</name>
<tag-class> cn.itcast.chapter09.classisctag.ToUpperCase</tag-class>
<body-content>JSP</body-content>
</tag>
(3)編寫JSP頁面toUpperCase.jsp
在JSP頁面中使用標簽,在標簽體中寫人26個小寫的英文字母,如下例所示。
<%@page language="java" pageEncoding="GBK"%>
<%@taglib uri= "http://www.itcast.cn" prefix="itcast"%>
<html>
<head>
<title>HelloWorld Tag</title>
</head>
<body>
<itcast: toUpperCase>
abcde fghij klmnopqrstuvwxyz
</itcast:toUpperCase>
</body>
</html>
(4)啟動Tomcat服務器,在瀏覽器地址欄中輸人URL地址“http://localhost:8080/chapter09/toUpperCase.jsp”訪問toUpperCase.jsp頁面,從運行結(jié)果可以看出,自定義標簽成功地將標簽體中的小寫英文字母轉(zhuǎn)換為大寫。
*請認真填寫需求信息,我們會在24小時內(nèi)與您取得聯(lián)系。