16°

jsp标签(自定义标签)

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

&lt;tlib-version&gt;1.0&lt;/tlib-version&gt;
&lt;short-name&gt;myshortname&lt;/short-name&gt;
&lt;uri&gt;dateuri&lt;/uri&gt;

&lt;tag&gt;
    &lt;name&gt;date&lt;/name&gt;
    &lt;tag-class&gt;taeyeon.com.jsp.tld.DateTag&lt;/tag-class&gt;
    &lt;body-content&gt;empty&lt;/body-content&gt;
    &lt;attribute&gt;
        &lt;name&gt;formateDate&lt;/name&gt;&lt;!--属性名称--&gt;
        &lt;required&gt;true&lt;/required&gt;&lt;!--是否为必输项--&gt;
        &lt;rtexprvalue&gt;true&lt;/rtexprvalue&gt;&lt;!--是否支持表达式输出--&gt;
    &lt;/attribute&gt;
&lt;/tag&gt;

</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类中定义的常量和方法
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的执行图:

五、有标签体的标签库

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

&lt;tlib-version&gt;1.0&lt;/tlib-version&gt;
&lt;short-name&gt;myshortname&lt;/short-name&gt;
&lt;uri&gt;bodytag&lt;/uri&gt;
&lt;tag&gt;
    &lt;name&gt;body&lt;/name&gt;
    &lt;tag-class&gt;taeyeon.com.jsp.tld.BodyTag&lt;/tag-class&gt;
    &lt;body-content&gt;JSP&lt;/body-content&gt;&lt;!-- 执行标签体内容--&gt;
    &lt;attribute&gt;
        &lt;name&gt;scope&lt;/name&gt;
        &lt;required&gt;true&lt;/required&gt;
        &lt;rtexprvalue&gt;true&lt;/rtexprvalue&gt;
    &lt;/attribute&gt;
    &lt;attribute&gt;
        &lt;name&gt;name&lt;/name&gt;
        &lt;required&gt;true&lt;/required&gt;
        &lt;rtexprvalue&gt;true&lt;/rtexprvalue&gt;
    &lt;/attribute&gt;
&lt;/tag&gt;

</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&lt;?&gt; getIter() {
			return iter;
	}

	public void setIter(Iterator&lt;?&gt; 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 &amp;&amp; value instanceof List&lt;?&gt;) {
					this.iter = ((List&lt;?&gt;) 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">

&lt;tlib-version&gt;1.0&lt;/tlib-version&gt;
&lt;short-name&gt;myshortname&lt;/short-name&gt;
&lt;uri&gt;iteratortag&lt;/uri&gt;

&lt;tag&gt;
    &lt;name&gt;iterator&lt;/name&gt;
    &lt;tag-class&gt;taeyeon.com.jsp.tld.IterationTag&lt;/tag-class&gt;
    &lt;body-content&gt;JSP&lt;/body-content&gt;
    &lt;attribute&gt;
        &lt;name&gt;name&lt;/name&gt;
        &lt;required&gt;true&lt;/required&gt;
        &lt;rtexprvalue&gt;true&lt;/rtexprvalue&gt;
    &lt;/attribute&gt;
    &lt;attribute&gt;
        &lt;name&gt;scope&lt;/name&gt;
        &lt;required&gt;true&lt;/required&gt;
        &lt;rtexprvalue&gt;true&lt;/rtexprvalue&gt;
    &lt;/attribute&gt;
&lt;/tag&gt;

</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的扩充方法和常量
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类中。

本文由【Y】发布于开源中国,原文链接:https://my.oschina.net/u/4045839/blog/3137007

全部评论: 0

    我有话说: