JSTL自定義標籤

    這節咱們總結一下JSTL自定義標籤相關內容。html

1. 自定義標籤簡介

        自定義標籤主要用於移除JSP頁面中的Java代碼。Jsp頁面主要是用來顯示給前臺的,若是裏面有過多的java代碼的話,會顯得很亂,可是沒有java代碼也沒法獲取相關數據或完成相關操做。那麼這時候咱們就能夠本身定義一個標籤,來完成須要用java代碼完成的事情,這樣Jsp頁面就會清潔不少,可讀性也更強。JSP中使用自定義標籤移除只須要完成如下兩個步驟:java

        1)編寫一個實現Tag接口的java類(標籤處理類)
        2)編寫標籤庫描述符(tld)文件,在tld文件中對標籤處理類進行描述。web

2. 一個簡單實例

        咱們寫一個簡單的實例來快速理解自定義標籤:輸出客戶機的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框架的標籤庫會遇到。下面咱們來看一看簡單標籤的相關內容。

4. 簡單標籤

        簡單標籤接口SimpleTag與上面提到的標籤接口不一樣,SimpleTag標籤提供了一個簡單的doTag()方法代替了doStartTag和doEndTag方法。全部標籤的邏輯、迭代、計算等等都在這個方法中運行。所以SimpleTag接口也能夠替代BodyTag接口。該接口中有如下方法:

  1. void doTag()  
  2. JspTag getParent()  
  3. void setJspBody(JspFragment jspBody):把標籤體經過該方法傳遞進來,咱們能夠在doTag方法中拿到jspBody對象即拿到了標籤體,而後能夠對標籤體作想作的事。包括上面全部功能  
  4. void setJspContext(JspContext pc)  
  5. 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 {  
  
    //用簡單標籤使用這個方法完成全部業務邏輯  
    @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);//再將流輸出給瀏覽器  
          
    }  
}  

5. 帶屬性的標籤

        自定義標籤能夠定義一個或多個屬性,這樣在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次

6. JSTL案例

        咱們來用自定義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> 

7.  打包標籤庫

        既然咱們能夠自定義標籤,那咱們定義好的標籤如何打包供其餘工程使用呢?這在開發中是很重要的。咱們按照下列步驟來打包自定義標籤庫:

        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包了,而後使用本身開發的標籤了。

8.  JSTL經常使用標籤庫

1)<c:out>

        <c:out>標籤用於輸出一段文本內容到pageContext對象當前保存的"out"對象中,即:將內容輸出給瀏覽器。該標籤有三個屬性可選,value屬性指定要輸出的內容;escapeXml指定是否將<、>、&、`等特殊字符進行html編碼轉換後再進行輸出,默認爲true;default屬性指定若是value屬性的值爲null時所輸出的默認值。該標籤主要用於escapeXml屬性和default屬性

2)<c:set>

        <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 }  

3) <c:remove>

        <c:remove>標籤用於刪除各類web域中的屬性:

<c:remove var="varName" [scope="{page|request|session|application}"]/>  

4)<c:if>

        <c:if>標籤能夠判斷是否執行標籤體

<c:if test="${user==null}" var="result" scope="page">  
    xxx  
</c:if>  

   若是test中的表達式爲真則執行標籤體,另外將test的值保存在page域中,保存參數爲result,能夠經過${result}獲取保存的值。

5)<c:choose>

        標籤用於構造條件判斷語句

<c:choose>  
    <c:when test="${count == 0}">  
        對不起,沒有符合您要求的記錄  
    </c:when>  
    <c:otherwise>  
        符合您要求的記錄共有${count}條  
    </c:otherwise>  
</c:choose>  

6)<c:forEach>

        <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>  

7)<c:param>標籤

        在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部分的內容暫時就總結到這吧,若有錯誤之處,歡迎留言指正~

相關文章
相關標籤/搜索