這節咱們總結一下JSTL自定義標籤相關內容。html
自定義標籤主要用於移除JSP頁面中的Java代碼。Jsp頁面主要是用來顯示給前臺的,若是裏面有過多的java代碼的話,會顯得很亂,可是沒有java代碼也沒法獲取相關數據或完成相關操做。那麼這時候咱們就能夠本身定義一個標籤,來完成須要用java代碼完成的事情,這樣Jsp頁面就會清潔不少,可讀性也更強。JSP中使用自定義標籤移除只須要完成如下兩個步驟:java
1)編寫一個實現Tag接口的java類(標籤處理類);
2)編寫標籤庫描述符(tld)文件,在tld文件中對標籤處理類進行描述。web
咱們寫一個簡單的實例來快速理解自定義標籤:輸出客戶機的IP。按照上面描述的兩個步驟,咱們首先編寫一個實現Tag接口的java類:api
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; } @Override public int doEndTag() throws JspException { return 0; } @Override public Tag getParent() { return null; } @Override public void release() { } @Override public void setPageContext(PageContext arg0) { this.pageContext = arg0; } @Override 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頁面中就能夠導入並使用自定義標籤了:tomcat
<%@ 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. 自定義標籤功能擴展session
開發人員在編寫JSP頁面時,常常還須要在頁面中引入一些邏輯,如:app
控制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 { @Override public int doStartTag() throws JspException { // 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 { @Override public int doEndTag() throws JspException { // 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; @Override public int doAfterBody() throws JspException { i--; if(i >= 0) return IterationTag.EVAL_BODY_AGAIN; else return IterationTag.SKIP_BODY; } @Override public int doStartTag() throws JspException { 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 { @Override public int doStartTag() throws JspException { return BodyTag.EVAL_BODY_BUFFERED; } @Override public int doEndTag() throws JspException { //拿到標籤體 String content = this.getBodyContent().getString(); String result = content.toUpperCase(); try { this.pageContext.getOut().write(result); } catch (IOException e) { throw new RuntimeException(e); } return Tag.EVAL_PAGE; } }
注:以上擴展功能的知識點在JSP2.0後已經被淘汰掉了,會有一個新的簡單標籤(SimpleTag)接口能夠更方便的實現。可是以上的知識點在Struts框架中還在使用。在學Struts框架的標籤庫會遇到。下面咱們來看一看簡單標籤的相關內容。
簡單標籤接口SimpleTag與上面提到的標籤接口不一樣,SimpleTag標籤提供了一個簡單的doTag()方法代替了doStartTag和doEndTag方法。全部標籤的邏輯、迭代、計算等等都在這個方法中運行。所以SimpleTag接口也能夠替代BodyTag接口。該接口中有如下方法:
JspContext是pageContext的父類,執行順序:JSP引擎首先經過uri和viewIP標籤名去找tld文件,在tld中經過viewIP找到ViewIPTag類,該類中,首先調用setJspContext方法把頁面的pageContext傳遞進來,再調用setParent把父標籤傳遞進來(沒有則不傳),而後再調用setJspBody方法,把表明標籤體的jspFragment對象傳遞進去,至此完成了標籤的初始化工做。而後開始執行標籤,即調用doTag方法。下面咱們使用簡單標籤擴展上面的功能。
1)控制JSP頁面某一部份內容是否執行
//控制標籤體是否執行 public class SimpleTagDemo1 extends SimpleTagSupport { //用簡單標籤使用這個方法完成全部業務邏輯 @Override 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默認寫給瀏覽器。不調用該方法即不運行標籤體 } }
在simple.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>/simpleitcast</uri> <tag> <name>demo1</name> <tag-class>web.simpletag.SimpleTagDemo1</tag-class> <body-content>scriptless</body-content> </tag> </taglib>
2)控制整個JSP頁面是否執行
public class SimpleTagDemo4 extends SimpleTagSupport { @Override public void doTag() throws JspException, IOException { throw new SkipPageException();//拋出個SkipPageException異常即不會執行下面的JSP頁面,不然會執行 } }
tld文件中的配置略,跟上面同樣的……再也不贅述。
3)控制JSP頁面內容重複執行
//控制標籤體執行10次 public class SimpleTagDemo3 extends SimpleTagSupport { @Override public void doTag() throws JspException, IOException { JspFragment jf = this.getJspBody(); for(int i = 0; i < 10; i++) { jf.invoke(null); } } }
4)修改JSP頁面內容輸出
//將標籤體內容改爲大寫 public class SimpleTagDemo3 extends SimpleTagSupport { @Override public void doTag() throws JspException, IOException { JspFragment jf = this.getJspBody(); 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; } @Override public void doTag() throws JspException, IOException { for(int i = 0; i < count; i++){ this.getJspBody().invoke(null); } } }
tld文件中配置以下:
<tag> <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> </tag>
在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; } @Override public void doTag() throws JspException, IOException { //看來訪者是從哪一個頁面來的 PageContext pageContext = (PageContext)this.getJspContext(); 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頁面:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>My JSP 'index.jsp' starting page</title> </head> <body> This is my JSP page. <br> <a href="${pageContext.request.contextPath }/test.jsp">內容</a> <!--掉轉到1.jsp--> </body> </html>
再看一下test.jsp:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <%@ taglib uri="/simpleitcast" prefix="it"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <it:referer site="http://localhost:8080/" page="/index.jsp"/> <html> <head> <title>防盜鏈</title> </head> <body> 這是內容 </body> </html>
既然咱們能夠自定義標籤,那咱們定義好的標籤如何打包供其餘工程使用呢?這在開發中是很重要的。咱們按照下列步驟來打包自定義標籤庫:
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} </c:forEach>
在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"/>
<c:url>標籤用於在JSP頁面中構造一個url地址,其主要目的是實現url重寫。url重寫就是將會話標識號以參數形式附加在url地址後面(關於url重寫,在總結session技術的時候有寫到)。它有以下屬性:
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部分的內容暫時就總結到這吧,若有錯誤之處,歡迎留言指正~