jsp標籤(自定義標籤)

jsp標籤

1、簡介

  1. jsp標籤庫是使用XML語法格式完成程序操做的一種方法,其使用的形式相似於javaBean的使用語法jsp:userBean,主要目的就是爲了減小頁面的Scriptlet代碼,使程序更加便於理解和修改。

2、空標籤

  1. 要實現一個自定義標籤,能夠直接繼承javax.servlet.jsp.tagext.TagSupport類,重寫doStartTag()方法的做用是在標籤起始時進行調用。
  2. 一個標籤類定義完成後,下面就要編寫標籤描述文件了,在.tld文件中能夠描述標籤名稱、簡介、處理類、標籤使用屬性等。
  3. 在jsp頁面中映射該標籤<%@ taglib prefix="標籤前綴" uri="tld文件路徑"%>,而且調用<標籤前綴 jsp標籤使用名稱>
package taeyeon.com.jsp.tld;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.TagSupport;
import java.io.IOException;

public class HelloTag extends TagSupport {
		@Override
		public int doStartTag() throws JspException {
				JspWriter writer = super.pageContext.getOut(); // 取得jsp的輸出流對象
				try {
						writer.println("<h2>Hello   World!</h2>");
				} catch (IOException e) {
						e.printStackTrace();
				}
				return super.SKIP_BODY;//沒有標籤體
		}
}
<?xml version="1.0" encoding="ISO-8859-1"?>

<taglib xmlns="http://java.sun.com/xml/ns/javaee"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd"
        version="2.1">

    <tlib-version>1.0</tlib-version><!--標籤庫版本-->
    <short-name>hello</short-name><!--標籤庫在tld中的描述名稱-->
    <uri>http://mycompany.com</uri><!--jsp頁面中taglib標籤中的uri映射路徑,可本身定義。只要知足書寫標準-->
<tag>
    <name>hello</name><!--在jsp中使用的名稱-->
    <tag-class>taeyeon.com.jsp.tld.HelloTag</tag-class><!--標籤指向的操做類-->
    <body-content>empty</body-content><!--是否有標籤體-->
</tag>
</taglib>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="hello" uri="/WEB-INF/tld/hello.tld" %>
<html>
<head>
    <title>第一個tld標籤頁面</title>
</head>
<body>
<hello:hello/>
</body>
</html>

輸出html

Hello World!
  1. 有時候uri太長,後期很差維護,咱們就能夠在web.xml文件中映射該uri,給該uri取虛擬名,之後在jsp頁面中引用時就能夠直接書寫該虛擬名,來訪問該標籤描述文件。
<!--映射tag的uri,操做tld文件-->
  <jsp-config>
    <taglib>
      <taglib-uri>hello_uri</taglib-uri>
      <taglib-location>/WEB-INF/tld/hello.tld</taglib-location>
    </taglib>
  </jsp-config>

注意:這裏在web.xml文件中配置的 <jsp-config>,在web.xml2.4版本以前是能夠書寫的,可是在以後書寫會報錯,由於tld文件中新增了一個標籤<uri>能夠直接映射jsp頁面中的引用uri,因此只要在tld文件中書寫就能夠了。具體在tld文本的那一個版本修改的有興趣的能夠本身查閱一下。前端

  1. 當tld文件第一次運行以後會被加載到jvm中,第二次調用的時候就不須要重複加載,可直接使用。

3、定義有屬性的標籤

  1. 例如:<jsp:forward page="">語句中的page就是一個屬性,在具體的標籤中咱們也要經常使用到屬性。下面咱們舉例一個日期格式化標籤。
    • 建立格式化日期標籤類
package taeyeon.com.jsp.tld;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.TagSupport;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

public class DateTag extends TagSupport {
		private String formateDate;

		public String getFormateDate() {
				return formateDate;
		}

		public void setFormateDate(String formateDate) {
				this.formateDate = formateDate;
		}

		@Override
		public int doStartTag() throws JspException {
				LocalDateTime date = LocalDateTime.now();
				DateTimeFormatter formatter = DateTimeFormatter.ofPattern(this.formateDate);
				try {
						super.pageContext.getOut().write(date.format(formatter));
				} catch (IOException e) {
						e.printStackTrace();
				}
				return TagSupport.SKIP_BODY;
		}
}
  • 書寫tld文件
<?xml version="1.0" encoding="ISO-8859-1"?>

<taglib xmlns="http://java.sun.com/xml/ns/javaee"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd"
        version="2.1">

    <tlib-version>1.0</tlib-version>
    <short-name>myshortname</short-name>
    <uri>dateuri</uri>

    <tag>
        <name>date</name>
        <tag-class>taeyeon.com.jsp.tld.DateTag</tag-class>
        <body-content>empty</body-content>
        <attribute>
            <name>formateDate</name><!--屬性名稱-->
            <required>true</required><!--是否爲必輸項-->
            <rtexprvalue>true</rtexprvalue><!--是否支持表達式輸出-->
        </attribute>
    </tag>

</taglib>

-引用tldjava

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="date" uri="dateuri" %>
<html>
<head>
    <title>帶屬性的標籤體</title>
</head>
<body>
<h2><date:date formateDate="yyyy-MM-dd HH:mm:ss"/></h2>
</body>
</html>
  • 輸出
2019-12-02 17:03:28

注意:這裏沒有用SimpleDateFormat類來格式化時期,實用爲SimpleDateFormat類時非線程安全的,而在jdk1.8以後提供了新的DateTimeFormatter類,該類線程安全也是做用於日期的格式化,二者的具體不一樣和使用,怎樣讓SimpleDateFormat變的線程安全能夠參考我後面博文寫的文章web

  1. rtexprvalue標籤值爲true時,則支持表達式輸出<名稱: tld中name名稱 屬性="${}"/屬性="<%= %>"/>

4、TagSupport類

  1. 基本的標籤掌握以後,能夠發現標籤的實現都須要繼承TagSupport這個類,因此TagSupport類是整個標籤編程的核心類:public class TagSupport extends Object implements IterationTag,Serializable
  2. TagSupport類同時實現了IterationTag,Serializable兩個接口,IterationTag接口定義以下:
public interface IterationTag extends Tag {
    public final static int EVAL_BODY_AGAIN = 2;
     int doAfterBody() throws JspException;
  1. Tag接口定義以下:
public interface Tag extends JspTag {
    public final static int SKIP_BODY = 0;
    public final static int EVAL_BODY_INCLUDE = 1;
    public final static int SKIP_PAGE = 5;
    public final static int EVAL_PAGE = 6;
    void setPageContext(PageContext pc);
    void setParent(Tag t);
    Tag getParent();
    int doStartTag() throws JspException;
    int doEndTag() throws JspException;
    void release();
    }
  1. TagSupport類中定義的常量和方法
NO 常量或方法 類型 描述
1 protected PageContext pageContext 屬性 表示PageContext對象,能夠操做四種屬性範圍
2 public static final int SKIP_BODY 常量 忽略標籤體內容,將操做轉交給doEndTag()
3 public static final int EVAL_BODY_INCLUDE 常量 正常執行標籤體操做,但不處理任何運算
4 public final static int SKIP_PAGE 常量 全部在jsp上操做都將中止,會將全部輸出的內容馬上顯示在瀏覽器上
5 public final static int EVAL_PAGE 常量 正常執行jsp頁面
6 public final static int EVAL_BODY_AGAIN 常量 重複執行標籤內容,會再次調用doAfterBody(),直到出現SKIP_BODY爲止
7 public abstract int doStartTag() throws JspException 方法 處理標籤開始部分
8 public abstract int doEndTag() throws JspException 方法 處理標籤結束部分
9 public abstract int doAfterBody() throws JspException 方法 處理標籤主體部分
10 public abstract void release() 方法 釋放標籤資源
  • doStartTag():此方法在標籤開始時執行,有以下兩種返回值。
    • SKIP-BODY:表示忽略標籤體的內容,而將執行權交給doEndTag()方法。
    • EVAL_BODY_INCLUDE:表示執行標籤體內容
  • doAfterBody():此方法是子接口Iteration與父接口Tag的差異所在,用來重複執行標籤體內容,返回值有:
    • SKIP_BODY:忽略標籤體內容,而將執行權交給doEndTag()方法。
    • EVAL_BODY_AGAIN:重複調用doAfterBody()方法,一支到返回值爲SKIP_BODY爲止。
  • doEndTag():標籤體結束時執行,有人以下返回值:
    • SKIP_PAGE:直接終止jsp頁面執行,將全部輸出值馬上回傳到瀏覽器上。
    • EVAL_PAGE:表示jsp能夠正常的運行結束。
  • release():表示標籤處理類所佔用的資源所有被釋放,等待下一次的調用。
  1. 下圖爲接口Iteration的執行圖:

5、有標籤體的標籤庫

  1. 存在標籤體則就代表該標籤之間時存在內容的。下面咱們就舉例來看一看:
    • 標籤處理類
package taeyeon.com.jsp.tld;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.TagSupport;

public class BodyTag extends TagSupport {
		private String name;
		private String scope;

		public String getScope() {
				return scope;
		}

		public void setScope(String scope) {
				this.scope = scope;
		}

		public String getName() {
				return name;
		}

		public void setName(String name) {
				this.name = name;
		}

		@Override
		public int doStartTag() throws JspException {
				Object value=null;
				if("page".equals(this.scope)){
						value=super.pageContext.getAttribute(name , PageContext.PAGE_SCOPE);
				}else if("request".equals(this.scope)){
						value=super.pageContext.getAttribute(name , PageContext.REQUEST_SCOPE);
				}else if("session".equals(this.scope)){
						value=super.pageContext.getAttribute(name , PageContext.SESSION_SCOPE);
				}else if("application".equals(this.scope)){
						value=super.pageContext.getAttribute(name , PageContext.APPLICATION_SCOPE);
				}
				if(value==null){
						return super.SKIP_BODY;
				}
				else {
						return  super.EVAL_BODY_INCLUDE;
				}
		}
}
  • 書寫tld文件
<?xml version="1.0" encoding="ISO-8859-1"?>

<taglib xmlns="http://java.sun.com/xml/ns/javaee"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd"
        version="2.1">

    <tlib-version>1.0</tlib-version>
    <short-name>myshortname</short-name>
    <uri>bodytag</uri>
    <tag>
        <name>body</name>
        <tag-class>taeyeon.com.jsp.tld.BodyTag</tag-class>
        <body-content>JSP</body-content><!-- 執行標籤體內容-->
        <attribute>
            <name>scope</name>
            <required>true</required>
            <rtexprvalue>true</rtexprvalue>
        </attribute>
        <attribute>
            <name>name</name>
            <required>true</required>
            <rtexprvalue>true</rtexprvalue>
        </attribute>
    </tag>
</taglib>
  • jsp頁面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="body" uri="bodytag" %>
<html>
<head>
    <title>有標籤體的標籤</title>
</head>
<body>
<%! String scope = "session";%>
<%
    session.setAttribute("name", "Yoona");
%>
<body:body name="name" scope="<%=scope%>">
    <h2>session屬性範圍</h2>
</body:body>
    </body>
    </html>
  • 頁面輸出
session屬性範圍

注意:編程

  1. 當咱們書寫的帶屬性字段的標籤時,咱們的tld文件裏的attribute標籤裏的name標籤要和標籤處理類裏的屬性字段名同樣,否則前端會報500錯誤。
  2. 當咱們把scope換成標籤處理類中不存在的分支return常數SKIP_BODY,那麼這裏就不會執行標籤體了,頁面顯示爲空,對於該例子來講

6、開發迭代標籤

  1. 在程序中開發迭代輸出是常見的一種輸出形式,jsp的一個主要功能就是輸出,爲了不大量的scriptlet代碼的出現,使用迭代標籤是頗有價值的。
  • 開發迭代標籤處理類
package taeyeon.com.jsp.tld;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.TagSupport;
import java.util.Iterator;
import java.util.List;

public class IterationTag extends TagSupport {
		private String name;
		private String scope;
		private Iterator<?> iter;

		public String getName() {
				return name;
		}

		public void setName(String name) {
				this.name = name;
		}

		public String getScope() {
				return scope;
		}

		public void setScope(String scope) {
				this.scope = scope;
		}

		public Iterator<?> getIter() {
				return iter;
		}

		public void setIter(Iterator<?> iter) {
				this.iter = iter;
		}

		@Override
		public int doStartTag() throws JspException {
				Object value = null;
				if ("page".equals(this.scope)) {
						value = super.pageContext.getAttribute(name, PageContext.PAGE_SCOPE);
				} else if ("request".equals(this.scope)) {
						value = super.pageContext.getAttribute(name, PageContext.REQUEST_SCOPE);
				} else if ("session".equals(this.scope)) {
						value = super.pageContext.getAttribute(name, PageContext.SESSION_SCOPE);
				} else if ("application".equals(this.scope)) {
						value = super.pageContext.getAttribute(name, PageContext.APPLICATION_SCOPE);
				}
				if (value != null && value instanceof List<?>) {
						this.iter = ((List<?>) value).iterator();
						if (iter.hasNext()) {
								super.pageContext.setAttribute("msg", iter.next());
								return super.EVAL_BODY_INCLUDE;
						} else {
								return super.SKIP_BODY;
						}
				} else {
						return super.SKIP_BODY;
				}
		}

		@Override
		public int doAfterBody() throws JspException {
				if (iter.hasNext()) {
						super.pageContext.setAttribute("msg", iter.next());
						return super.EVAL_BODY_AGAIN;
				} else {
						return super.SKIP_BODY;
				}
		}
}
  • 書寫tld文件
<?xml version="1.0" encoding="ISO-8859-1"?>

<taglib xmlns="http://java.sun.com/xml/ns/javaee"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd"
        version="2.1">

    <tlib-version>1.0</tlib-version>
    <short-name>myshortname</short-name>
    <uri>iteratortag</uri>

    <tag>
        <name>iterator</name>
        <tag-class>taeyeon.com.jsp.tld.IterationTag</tag-class>
        <body-content>JSP</body-content>
        <attribute>
            <name>name</name>
            <required>true</required>
            <rtexprvalue>true</rtexprvalue>
        </attribute>
        <attribute>
            <name>scope</name>
            <required>true</required>
            <rtexprvalue>true</rtexprvalue>
        </attribute>
    </tag>
</taglib>
  • jsp頁面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="iterator" uri="iteratortag" %>
<html>
<head>
    <title>迭代標籤庫</title>
</head>
<body>
<%
    String name1 = "session";
    List<String> list = new ArrayList<String>();
    list.add("18");
    list.add("yoona");
    list.add("Korea");
    session.setAttribute("name", list);
%>
<iterator:iterator name="name" scope="<%=name1%>">
    <h2>${msg}</h2>
</iterator:iterator>
</body>
</html>
  • 頁面輸出
18
yoona
Korea

在獲取屬性值的時候必定要注意訪問域的問題,否則有可能獲取不到值。瀏覽器

7、BodyTagSupport類

  1. BodyTagSupport是TagSupport類的子類,經過繼承BodyTagSupport類實現的標籤能夠直接處理標籤內容的數據,定義以下:
public class BodyTagSupport extends TagSupport implements BodyTag
  1. BodyTag接口的定義以下:
public interface BodyTag extends IterationTag {
    public final static int EVAL_BODY_TAG = 2;
    public final static int EVAL_BODY_BUFFERED = 2;
    void setBodyContent(BodyContent b);
    void doInitBody() throws JspException;
     }
  1. BodyTagSupport的擴充方法和常量
NO 方法 類型 描述
1 public final static int EVAL_BODY_BUFFERED 常量 表示標籤體的內容應該被處理,全部的處理結果都將保存在BodyContent類中
2 protected BodyContent bodyContent 屬性 存放標籤體的處理結果
3 public JspWriter getPreviousOut() 方法 取得JspWriter的輸出流對象
  1. 在BodyTagSupport類中定義一個bodyContent的受保護的屬性,而bodyContent時BodyContent類的對象,以下:
public abstract class BodyContent extends JspWriter
  1. BodyContent是JspWriter類的子類,能夠直接打印和輸出基本類型與對象值,可是BodyContent類與JspWriter類的區別在於,BodyContent類的任何寫入內容並不自動像頁面輸出,以下:
NO 方法 類型 描述
1 public abstract void writeOut(Writer out) throws IOException 方法 指定BodyContent內容的輸出流對象,並進行內容輸出
2 public abstract String getString() 將全部內容變爲String類型
3 public abstract Reader getReader() 方法 將內容變爲Reader對象
  1. BodyTag接口的執行流程圖以下: 從圖中能夠看出來,當返回值爲EVAL_BODY_BUFFERED時則會將全部的處理內容都保存道BodyContent類中,而且返回執行doAfterBody()方法;而若是doStartTag()方法返回的是EVAL_BODY_INCLUDE,則就不會保存到BodyContent類中。
相關文章
相關標籤/搜索