版權聲明:尊重部落客原創文章,轉載請注明出處哦~http://blog.csdn.net/eson_15/article/details/51264254
這節我們總結一下jstl自定義标簽相關内容。
1)編寫一個實作tag接口的java類(标簽處理類);
2)編寫标簽庫描述符(tld)檔案,在tld檔案中對标簽處理類進行描述。
我們寫一個簡單的執行個體來快速了解自定義标簽:輸出客戶機的ip。按照上面描述的兩個步驟,我們首先編寫一個實作tag接口的java類:
public class viewiptag implements tag {
private pagecontext pagecontext;
@override
public int dostarttag() throws jspexception {
httpservletrequest request = (httpservletrequest)pagecontext.getrequest(); //擷取request
jspwriter out = pagecontext.getout(); //擷取out</span>
string ip = request.getremoteaddr(); //通過request擷取客戶機的ip
try {
out.write(ip); //寫到浏覽器
} catch (ioexception e) {
throw new runtimeexception(e);
}
return 0;
}
public int doendtag() throws jspexception {
}
public tag getparent() {
return null;
public void release() {
public void setpagecontext(pagecontext arg0) {
this.pagecontext = arg0;
public void setparent(tag arg0) {
}
寫好了java類,我們來編寫标簽庫描述符(tld)檔案,在web-inf目錄下建立一個tld檔案,在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">
<description>a tag library exercising simpletag handlers.</description>
<tlib-version>1.0</tlib-version>
<short-name>simpletaglibrary</short-name>
<uri>/test</uri> <!--為該标簽配一個uri-->
<tag>
<name>viewip</name> <!-- 為标簽處理器類配一個标簽名 -->
<tag-class>web.tag.viewiptag</tag-class>
<body-content>empty</body-content>
</tag>
</taglib>
這樣我們在jsp頁面中就可以導入并使用自定義标簽了:
<%@ page language="java" import="java.util.*" pageencoding="utf-8"%>
<%@ taglib uri="/test" prefix="test"%> <!--uri與tld檔案中配的uri相同-->
<!doctype html public "-//w3c//dtd html 4.01 transitional//en">
<html>
<head>
<title>輸出客戶機的ip</title>
</head>
<body>
您的ip是:<test:viewip/>
<!--該标簽相當于執行下面腳本代碼-->
<%
string ip = request.getremoteaddr();
out.write(ip);
%>
</body>
</html>
到這裡,應該就能清楚了自定義标簽的定義和配置了。我們來分析一下執行順序:jsp引擎首先通過uri和viewip标簽名去找tld檔案,在tld中通過viewip找到viewiptag類,該類中,首先調用setpagecontext方法把頁面的pagecontext傳遞進來,再調用setparent把父标簽傳遞進來(沒有則不傳),至此完成了标簽的初始化工作。然後調用dostarttag和doendtag方法,開始和結束标簽,最後調用release方法釋放标簽,運作時所占的資源。
3. 自定義标簽功能擴充
開發人員在編寫jsp頁面時,經常還需要在頁面中引入一些邏輯,如:
控制jsp頁面某一部分内容是否執行;
控制整個jsp頁面是否執行;
控制jsp頁面内容重複執行;
修改jsp頁面内容輸出。
即:自定義标簽除了可以移除jsp頁面的java代碼外,還可以實作以上功能。下面我們一個個來分析:
1)控制jsp頁面某一部分内容是否執行
建立一個标簽處理類tagdemo1繼承tagsupport類(tagsupport類已經實作了tag接口,不用直接去實作了),dostarttag方法中可以通過不同的傳回值類控制标簽體是否執行。傳回tag.eval_body_include表示執行标簽體内容,傳回tag.skip_body表示不執行。
//控制标簽體是否執行
public class tagdemo1 extends tagsupport {
// return tag.skip_body;//不執行标簽體内容
return tag.eval_body_include; //執行标簽體内容
然後在檔案中配置好:
<tag>
<name>demo1</name>
<tag-class>web.tag.tagdemo1</tag-class>
<body-content>jsp</body-content> <!--jsp表示标簽體内容為jsp-->
</tag>
這樣即可在jsp頁面中可以根據标簽處理類dostarttag方法的傳回值控制标簽體内容是否執行。
2)控制整個jsp頁面是否執行
同樣地,doendtag方法中可以通過不同的傳回值類控制下面的jsp頁面是否執行。傳回tag.eval_page表示執行下面的jsp頁面内容,傳回tag.skip_page表示不執行。如下:
//控制jsp頁面是否執行
public class tagdemo2 extends tagsupport {
// return tag.skip_page;//不執行jsp頁面
return tag.eval_page;//執行jsp頁面
}
3)控制jsp頁面内容重複執行
控制頁面内容重複執行的話tag接口就無法滿足了,需要實作它的子接口iterationtag接口,該接口中有個doafterbody方法來決定是否重複執行标簽體内容。如果該方法傳回iterationtag.eval_body_again則繼續執行,如果傳回iterationtag.skip_body則結束重複并跳轉到doendtag()方法執行了。不過不用直接去實作iterationtag接口,tagsupport類也實作了該接口,是以隻要讓标簽處理類繼承tagsupport類即可。需要注意的是除了覆寫doafterbody方法外,還得覆寫dostarttag方法并傳回tag.eval_body_include。因為隻有允許标簽體執行才能重複執行。如下:
//控制頁面内容重複執行
public class tagdemo3 extends tagsupport {
int i = 5;
public int doafterbody() throws jspexception {
i--;
if(i >= 0)
return iterationtag.eval_body_again;
else
return iterationtag.skip_body;
return tag.eval_body_include;
4)修改jsp頁面内容輸出
修改jsp頁面内容輸出的話iterationtag接口就無法滿足了,需要實作它的子接口bodytag接口,該接口增加了兩個方法:doinitbody()和setbodycontent(bodycontent b)方法。如果dostarttag方法傳回bodytag.eval_body_buffered,則bodycontent對象就會被jsp翻譯過後的servlet建立,即得到标簽體對象。在doendtag方法裡對标簽内容進行修改,可以通過this.getbodycontent().getstring()拿到轉成string的标簽體内容,然後對該内容進行修改,再将結果result通過this.pagecontext().getout().write(result)進行輸出,在doendtag方法裡傳回tag.eval_page。然後jsp翻譯過的servlet再把該bodycontent對象傳遞給setbodycontent方法完成修改。是以我們隻需要覆寫dostarttag和doendtag方法即可。該接口已經有個預設實作類bodytagsupport,編寫标簽處理類直接繼承這個類即可。如下:
//修改标簽内容
public class tagdemo4 extends bodytagsupport {
return bodytag.eval_body_buffered;
//拿到标簽體
string content = this.getbodycontent().getstring();
string result = content.touppercase();
this.pagecontext.getout().write(result);
}
return tag.eval_page;
}
注:以上擴充功能的知識點在jsp2.0後已經被淘汰掉了,會有一個新的簡單标簽(simpletag)接口可以更友善的實作。但是以上的知識點在struts架構中還在使用。在學struts架構的标簽庫會遇到。下面我們來看一看簡單标簽的相關内容。
簡單标簽接口simpletag與上面提到的标簽接口不同,simpletag标簽提供了一個簡單的dotag()方法代替了dostarttag和doendtag方法。所有标簽的邏輯、疊代、計算等等都在這個方法中運作。是以simpletag接口也可以替代bodytag接口。該接口中有以下方法:
void dotag()
jsptag getparent()
void setjspbody(jspfragment jspbody):把标簽體通過該方法傳遞進來,我們可以在dotag方法中拿到jspbody對象即拿到了标簽體,然後可以對标簽體做想做的事。包括上面所有功能
void setjspcontext(jspcontext pc)
void setparent(jsptag parent)
jspcontext是pagecontext的父類,執行順序:jsp引擎首先通過uri和viewip标簽名去找tld檔案,在tld中通過viewip找到viewiptag類,該類中,首先調用setjspcontext方法把頁面的pagecontext傳遞進來,再調用setparent把父标簽傳遞進來(沒有則不傳),然後再調用setjspbody方法,把代表标簽體的jspfragment對象傳遞進去,至此完成了标簽的初始化工作。然後開始執行标簽,即調用dotag方法。下面我們使用簡單标簽擴充上面的功能。
1)控制jsp頁面某一部分内容是否執行
public class simpletagdemo1 extends simpletagsupport {
//用簡單标簽使用這個方法完成所有業務邏輯
public void dotag() throws jspexception, ioexception {
//得到代表标簽體的jspfragment
jspfragment jf = this.getjspbody();
// pagecontext pagecontext = (pagecontext)this.getjspcontext(); //獲得pagecontext
// jf.invoke(pagecontext.getout());//将輸出流放到invoke方法中,寫給浏覽器
jf.invoke(null);//null預設寫給浏覽器。不調用該方法即不運作标簽體
}<span style="font-family:microsoft yahei;">
</span>
在simple.tld檔案中配置如下(與上面的基本一樣的):
<uri>/simpleitcast</uri>
<name>demo1</name>
<tag-class>web.simpletag.simpletagdemo1</tag-class>
<body-content>scriptless</body-content>
public class simpletagdemo4 extends simpletagsupport {
throw new skippageexception();//抛出個skippageexception異常即不會執行下面的jsp頁面,否則會執行
tld檔案中的配置略,跟上面一樣的……不再贅述。
//控制标簽體執行10次
public class simpletagdemo<span style="font-family:microsoft yahei;">3</span> extends simpletagsupport {
for(int i = 0; i < 10; i++) {
jf.invoke(null);
//将标簽體内容改成大寫
public class simpletagdemo3 extends simpletagsupport {
stringwriter sw = new stringwriter();
jf.invoke(sw);//不要invoke到浏覽器,先invoke到自己的流裡,然後修改修改再輸出
string content = sw.getbuffer().tostring();//獲得标簽體的string内容
content = content.touppercase();
pagecontext pagecontext = (pagecontext)this.getjspcontext();
pagecontext.getout().write(content);//再将流輸出給浏覽器
自定義标簽可以定義一個或多個屬性,這樣在jsp頁面中應用自定義标簽時就可以設定這些屬性的值,通過這些屬性為标簽處理器傳遞參數資訊,進而提供标簽的靈活性和複用性。想要讓一個自定義标簽具有屬性,通常需要完成兩個任務即可:
1)在标簽處理器中編寫每個屬性對應的setter方法
2)在tld檔案中描述标簽的屬性
為自定義标簽定義屬性時,每個屬性都必須按照javabean的屬性命名方式,在标簽處理器中定義屬性名對應的setter方法,用來接收jsp頁面調用自定義标簽時傳遞進來的屬性值。例如屬性url,在标簽處理器類中就要定義相應的seturl(string url)方法。在标簽處理器中定義相應的set方法後,jsp引擎在解析執行開始标簽前,也就是調用dostarttag方法前,會調用set屬性方法,為标簽設定屬性。我們看下面的例子:
//通過屬性控制标簽體的執行次數
public class simpletagdemo5 extends simpletagsupport {
public int count; //<itcast:demo5 count="6"
public void setcount(int count) {//自動将屬性傳進來
this.count = count;
for(int i = 0; i < count; i++){
this.getjspbody().invoke(null);
tld檔案中配置如下:
<name>demo5</name>
<tag-class>web.simpletag.simpletagdemo5</tag-class>
<body-content>scriptless</body-content>
<attribute>
<name>count</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue> <!-- true的話jsp中該屬性隻可以為表達式,false隻能為靜态值 -->
<type>java.lang.integer</type> <!-- 指明參數的類型 -->
</attribute>
在jsp頁面中就可以使用标簽
<itcast:demo5 count="4">
xxxx
</itcast>
xxxx被輸出4次
我們來用自定義jstl标簽開發一個防盜鍊的标簽:如果用戶端直接通路http://localhost:8080/example/test.jsp,會被阻止,先跳轉到首頁index.jsp,再通路1.jsp
public class referertag extends simpletagsupport {
private string site;
private string page;
public void setsite(string site) {
this.site = site;
public void setpage(string page) {
this.page = page;
//看來訪者是從哪個頁面來的
httpservletrequest request = (httpservletrequest)pagecontext.getrequest();
string referer = request.getheader("referer");//得到是從哪個頁面來的
//判斷是否從自己的首頁過來的
if(referer == null || !referer.startswith(site)) {
httpservletresponse response = (httpservletresponse)pagecontext.getresponse();
string webroot = request.getcontextpath(); //example
if(page.startswith(webroot))
response.sendredirect(page);
else
response.sendredirect(webroot + page);
//重定向後,控制保護的頁面不要執行
throw new skippageexception();
看一下index.jsp頁面:
<head>
<title>my jsp 'index.jsp' starting page</title>
</head>
this is my jsp page. <br>
<a href="${pagecontext.request.contextpath }/test.jsp">内容</a> <!--掉轉到1.jsp-->
再看一下test.jsp:
<%@ taglib uri="/simpleitcast" prefix="it"%>
<it:referer site="http://localhost:8080/" page="/index.jsp"/>
<head>
<title>防盜鍊</title>
這是内容
既然我們可以自定義标簽,那我們定義好的标簽如何打包供其他工程使用呢?這在開發中是很重要的。我們按照下列步驟來打包自定義标簽庫:
a. 建立一個普通java project,将原來開發标簽庫工程的src目錄下的标簽處理類全部拷貝過來;
b. 在工程下建立一個lib目錄,把jsp和servlet兩個jar包(jsp-api.jar和servlet-api.jar)拷貝進來(在tomcat目錄\lib下);
c. 選中這兩個jar包,右擊->build path->add to path,變成兩個"奶瓶狀"即可;
d. 在工程下建立一個meta-inf目錄,将标簽的配置檔案(tld檔案)考進來;
e. 将整個工程導出:export->選擇java->jar file->next->右邊的classpath和project不用打鈎,然後選擇導出目錄即可導出。
這樣标簽庫的jar包就打包好了。
再建立一個web project,将剛剛打包好的jar包拷貝到web-inf\lib下,這樣在web-inf下建立一個jsp檔案,在該檔案裡就可以通過taglib導入剛剛的jar包了,然後使用自己開發的标簽了。
<c:out>标簽用于輸出一段文本内容到pagecontext對象目前儲存的"out"對象中,即:将内容輸出給浏覽器。該标簽有三個屬性可選,value屬性指定要輸出的内容;escapexml指定是否将<、>、&、`等特殊字元進行html編碼轉換後再進行輸出,預設為true;default屬性指定如果value屬性的值為null時所輸出的預設值。該标簽主要用于escapexml屬性和default屬性
<c:set>标簽用于把某一個對象存在指定的域範圍内,或者設定web域中的java.util.map類型的屬性對象或javabean類型的屬性對象的屬性。有如下屬性:
value:用于指定屬性的值;
scope:用于指定屬性所在的web域;
var:用于指定要設定的web域屬性的名稱;
target:用于指定要設定屬性的對象,這個對象必須是javabean對象或者java.util.map對象;
property:用于指定目前為對象設定的屬性名稱。
<!-- c:set标簽 :向web域中存資料,向map或bean中存資料-->
<c:set var="data" value="xxx" scope="page"/>
${pagescope.data }
<%
map map = new hashmap();
request.setattribute("map", map);
%>
<c:set property="data" value="yyy" target="${map }" />
${map.data }
<c:set property="name" value="eson_15" target="${person }"/>
${person.name }
<c:remove>标簽用于删除各種web域中的屬性:
<c:remove var="varname" [scope="{page|request|session|application}"]/>
<c:if>标簽可以判斷是否執行标簽體
<c:if test="${user==null}" var="result" scope="page">
xxx
</c:if>
如果test中的表達式為真則執行标簽體,另外将test的值儲存在page域中,儲存參數為result,可以通過${result}擷取儲存的值。
标簽用于構造條件判斷語句
<c:choose>
<c:when test="${count == 0}">
對不起,沒有符合您要求的記錄
</c:when>
<c:otherwise>
符合您要求的記錄共有${count}條
</c:otherwise>
</c:choose>
<c:foreach>标簽用于對一個集合對象中的元素進行循環疊代操作,或者按指定的次數重複疊代執行标簽體中的内容。它有如下屬性:
var屬性:指定目前疊代到的元素儲存到page域中的屬性名稱;
items屬性:将要疊代的集合對象;
begin屬性:如果指定items屬性,就從集合中的第begin個元素開始疊代,begin的索引值從0開始編号;如果沒有指定items屬性,就從begin指定的值開始疊代,直到end值結束。
step屬性:指定疊代的步長。
<!-- c:foreach标簽 -->
<c:foreach var="num" begin="1" end="10" step="1">
${num } <!-- 輸出1-10 -->
</c:foreach> <br>
list list = arrays.aslist("1","2");
request.setattribute("list", list);
<c:foreach var="index" begin="0" end="${fn.length(list) }">
${list[index] }
</c:foreach>
<c:foreach var="index" items="${list}">
${index}
在jsp頁面進行url的相關操作時,經常用在url位址後面附加一些參數。<c:param>标簽可以嵌套在<c:import>、<c:url>或<c:redirect>标簽内,為這些标簽所使用的url位址附加參數。<c:param>标簽在為一個url位址附加參數時,将自動對參數值進行url編碼,例如,如果傳遞的參數為“中國”,則将其轉換為"%d6%d0%b9%fa"後再附加到url位址後面,這也就是使用<c:param>标簽的最大好處。如:<c:param name="name" value="value"/>
value屬性:指定要構造的url;
var屬性:指定将構造出的url結果儲存到web域中的屬性名稱
scope屬性:指定将構造出的url結果儲存到那個web域中
<!-- c:url标簽(重點) -->
<a href="<c:url value="/servlet/servletdemo1"/>">點點</a>
<c:url value="/servlet/servletdemo2" var="servletdemo2"><!-- 如果沒有var,則會把位址直接打給浏覽器 -->
<c:param name="name" value="中國"/> <!-- 中國兩個字已經被url編碼了 -->
<c:param name="password" value="我是一個"></c:param>
</c:url>
<a href="${servletdemo2}">點點</a>
<c:redirect>标簽用于實作請求重定向。
url屬性:指定要轉發或重定向到的目标資源的url位址;
context:當要使用相對路徑重定向到同一個伺服器下的其他web應用程式中的資源時,context屬性指定其他web應用程式的名稱。
關于jstl部分的内容暫時就總結到這吧,如有錯誤之處,歡迎留言指正~
_____________________________________________________________________________________________________________________________________________________
-----樂于分享,共同進步!