jsp标签

一、简介

  1. jsp标签库是使用XML语法格式完成程序操作的一种方法,其使用的形式类似于javaBean的使用语法jsp:userBean,主要目的就是为了减少页面的Scriptlet代码,使程序更加便于理解和修改。

二、空标签

  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>

输出

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中,第二次调用的时候就不需要重复加载,可直接使用。

三、定义有属性的标签

  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>

-引用tld

<%@ 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变的线程安全可以参考我后面博文写的文章

  1. rtexprvalue标签值为true时,则支持表达式输出<名称: tld中name名称 属性="${}"/属性="<%= %>"/>

四、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类中定义的常量和方法
  • 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的执行图:jsp标签(自定义标签)-LMLPHP

五、有标签体的标签库

  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,那么这里就不会执行标签体了,页面显示为空,对于该例子来说

六、开发迭代标签

  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

在获取属性值的时候一定要注意访问域的问题,不然有可能获取不到值。

七、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的扩充方法和常量
  1. 在BodyTagSupport类中定义一个bodyContent的受保护的属性,而bodyContent时BodyContent类的对象,如下:
public abstract class BodyContent extends JspWriter
  1. BodyContent是JspWriter类的子类,可以直接打印和输出基本类型与对象值,但是BodyContent类与JspWriter类的区别在于,BodyContent类的任何写入内容并不自动像页面输出,如下:
  1. BodyTag接口的执行流程图如下:jsp标签(自定义标签)-LMLPHP从图中可以看出来,当返回值为EVAL_BODY_BUFFERED时则会将所有的处理内容都保存道BodyContent类中,并且返回执行doAfterBody()方法;而如果doStartTag()方法返回的是EVAL_BODY_INCLUDE,则就不会保存到BodyContent类中。
12-04 02:16