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类中。
相关文章
相关标签/搜索