自定义标签的功能拓展:

开发人员在编写JSP页面时,经常还需要在页面中引入一些逻辑,例如:
1.控制jsp页面某一部分内容是否执行。
2.控制整个jsp页面是否执行。
3.控制jap页面内容重复执行。
4.修改jsp页面内容输出。
自定义标签除了可以移除jsp页面代码外,它也可以实现以上功能。

上面4句话对应什么意思呢?我们使用实例说明一下(一下使用的是struts和jstl标签做例子):
1.控制jsp页面某一部分内容是否执行。
例如在页面中,我们希望有一些页面代码只有有权限的人才可以看得到,我们就可以使用自定义标签来完成这个操作:

<s:if test="user!=null">
   欢迎您!<s:property value="user,name"/>
</s:if>


2.控制整个jsp页面是否执行。


我们可以根据用户是不是有权限,来确定是否将整个页面给用户看


<s:if test="#session.SYS_USER!=null">
   <html>
      <head></head>
      <body>
          页面主代码......
      </body>
   </html>
</s:if>


3.控制jap页面内容重复执行。


比如让一段内容输出5遍:


<c:forEach var="i" begin="1" end="5">
   Item <c:out value="${i}"/><p>
</c:forEach>


结果


Item 1


Item 2


Item 3


Item 4


Item 5



4.修改jsp页面内容输出。


可以根据session是否有登录信息,而修改相应的登录信息:


<h2>登录状态:</h2>
<s:if test="#session.SYS_USER!=null">
    您已登录!
</s:if><s:else>
    您未登录!
</s:else>


我们下面就使用自定义标签来实现这四个功能。


1.使用标签控制页面内容是否输出实例


首先我们要开发“heddin”标签,然后让这个HelloWorld在jsp页面中不显示。


<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
<%@taglib uri="http://blog.csdn.net/acmman" prefix="zyg" %>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>使用标签控制页面内容是否输出</title>
  </head>
  
  <body>
     <zyg:heddin>
         HelloWorld!
     </zyg:heddin>
  </body>
</html>


首先说一下Tag接口的doStartTag()方法,它有一个int的返回值,如果想要处理标签体中的数据,就返回int型的“EVAL_BODY_INCLUDE”,如果不想处理,就返回“SKIP_BODY”。



(注意:Tag接口中定义了四个常量,分别是“EVAL_BODY_INCLUDE”、“EVAL_PAGE”、“SKIP_BODY”、“SKIP_PAGE”)



我们接下来为“heddin”标签写一个标签处理器类:


【自定义标签开发】03-自定义标签功能概述_html



我们重写doStartTag()方法,返回“EVAL_BODY_INCLUDE”参数:


package org.zyg.web.tag;

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

public class HeddinTag extends TagSupport {

	@Override
	public int doStartTag() throws JspException {
		
		return Tag.EVAL_BODY_INCLUDE;
	}
	
}


然后在zyg.tld中注册这个标签处理器类:


<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>zyg</short-name>
    <uri>http://blog.csdn.net/acmman</uri><!-- 标签绑定的uri,用于引入 -->
    
    
    <tag>
        <name>viewIP</name><!-- 标签名 -->
		<tag-class>org.zyg.web.tag.ViewIPTag</tag-class>
		<body-content>empty</body-content><!-- 有无标签体(单标签还是成对标签) -->
    </tag>
    
    <tag>
        <name>heddin</name><!-- 标签名 -->
		<tag-class>org.zyg.web.tag.HeddinTag</tag-class>
		<body-content>JSP</body-content><!-- 有无标签体(单标签还是成对标签) -->
    </tag>
    
</taglib>


我们将web重启,然后测试一下:


【自定义标签开发】03-自定义标签功能概述_java_02


发现我们是能够看到“HelloWorld”的,然后我们将标签处理库类的返回值换成“SKIP_BODY”:


@Override
public int doStartTag() throws JspException {
	
	return Tag.SKIP_BODY;
}


再重启Web应用,发现已经看不到HelloWorld了。


【自定义标签开发】03-自定义标签功能概述_html_03


以上就是使用标签控制页面内容是否输出的实现。



2.控制整个jsp是否输出实例


我们先来了解一下doEndTag()方法,这个方法也有一个返回值,是int类型,这个返回值控制的是“是否显示余下的jsp内容”,其中如果返回值是“EVAL_PAGE”,就执行余下的jsp内容,如果返回值是“SKIP_PAGE”,那么就不再执行余下的jsp代码。



于是我们先编辑一个jsp界面,然后加上我们的自定义标签<zyg:heddinPage/>:


<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
<%@taglib uri="http://blog.csdn.net/acmman" prefix="zyg" %>

<zyg:heddinPage/>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>控制整个jsp是否输出</title>
  </head>
  
  <body>
     这是我们的jsp整个页面
  </body>
</html>


然后创建一个自定义标签库类:


【自定义标签开发】03-自定义标签功能概述_jsp页面_04



重写Tag的doEngTag()方法,给它一个“SKIP_PAGE”的返回值:


package org.zyg.web.tag;

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

public class HeddinPageTag extends TagSupport {

	@Override
	public int doEndTag() throws JspException {
		
		return Tag.SKIP_PAGE;
	}
}

然后在zyg.tld中注册这个标签:


<tag>
        <name>heddinPage</name><!-- 标签名 -->
        <tag-class>org.zyg.web.tag.HeddinPageTag</tag-class>
        <body-content>empty</body-content><!-- 有无标签体(单标签还是成对标签) -->
</tag>


然后重启Web项目,访问该页面:


【自定义标签开发】03-自定义标签功能概述_html_05


我们右键点击查看源码,发现源码中也是空空如也:


【自定义标签开发】03-自定义标签功能概述_java_06


【自定义标签开发】03-自定义标签功能概述_java_07



我们将<zyg:heddinPage/>移到中间:


<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
<%@taglib uri="http://blog.csdn.net/acmman" prefix="zyg" %>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>控制整个jsp是否输出</title>
  </head>
  <zyg:heddinPage/>
  <body>
     这是我们的jsp整个页面
  </body>
</html>


再次访问页面,我们看到下面的代码是没有的:


【自定义标签开发】03-自定义标签功能概述_html_08


说明我们的jsp显示控制是有效的。


想放行的话,就将doEndTag()的返回值置为“EVAL_PAGE”即可,这里不再演示。



3.控制jap页面内容重复执行实例。


我们想让页面中的“哈哈哈,猴赛雷o(^▽^)o”重复执行5次:


<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
<%@taglib uri="http://blog.csdn.net/acmman" prefix="zyg" %>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>控制jsp页面内容重复执行</title>
  </head>


  <body>
  
     <zyg:repeat>
         哈哈哈,猴赛雷o(^▽^)o <br/>
     </zyg:repeat>


  </body>
</html>


我们来写<zyg:repeat>这个自定义标签。



这个功能仅仅依靠Tag接口是不可能完成的,所以sun公司又对Tag进行了拓展:


【自定义标签开发】03-自定义标签功能概述_html_09



想要迭代就需要其中的IterationTag接口,它在Tag的基础上扩充了一个常量EVAL_BODY_AGAIN和一个方法doAfterBody()。



我们使用IterationTag接口的EVAL_BODY_AGAIN常量和doAfterBody()方法既可以控制jsp页面中内容的重复。



doAfterBody()方法是在“标签体执行”动作之后,以及“结束标签”动作之前执行。如果这个方法返回“EVAL_BODY_AGAIN”常量,这个时候就会再把标签体执行一次,然后继续递归执行doAfterBody()方法。以此类推,只要这个方法返回“EVAL_BODY_AGAIN”常量它都会循环执行标签体内容,直到doAfterBody()方法返回“SKIP_BODY”常量之后才结束循环。



我们下面开始开发这个标签类,因为TagSupport类同时也实现了IterationTag接口,所以我们还是继承TagSupport类:


【自定义标签开发】03-自定义标签功能概述_html_10



我们在类中重写doStartTag()方法和doAfterBody()方法:


package org.zyg.web.tag;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.IterationTag;
import javax.servlet.jsp.tagext.Tag;
import javax.servlet.jsp.tagext.TagSupport;

//控制标签体执行5次
public class RepeatTag extends TagSupport {
	
	int x = 5;
	
	@Override
	public int doStartTag() throws JspException {
		
		return Tag.EVAL_BODY_INCLUDE;
	}

	@Override
	public int doAfterBody() throws JspException {

		x--;
		if(x>0){
			return IterationTag.EVAL_BODY_AGAIN;
		}else{
			return IterationTag.SKIP_BODY;
		}
	}
}


首先让doStartTag()方法返回EVAL_BODY_INCLUDE变量放行,然后在doAfterBody()方法中执行循环操作。



然后我们在zyg.tld中注册这个标签:


<tag>
    <name>repeat</name><!-- 标签名 -->
    <tag-class>org.zyg.web.tag.RepeatTag</tag-class>
    <body-content>JSP</body-content><!-- 有无标签体(单标签还是成对标签) -->
</tag>

然后重新发布一下Web工程,访问页面之后显示:


【自定义标签开发】03-自定义标签功能概述_html_11


这就是用自定义标签控制jap页面内容重复执行。




4.用标签修改jsp页面内容实例


开发新的标签将下面的小写字母变为大写:


<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
<%@taglib uri="http://blog.csdn.net/acmman" prefix="zyg" %>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>修改jsp页面内容</title>
  </head>
  
  <body>
     <zyg:upper>
         abcdefg
     </zyg:upper>
  </body>
  
</html>


这个时候我们需要BodyTag接口(它实现了Tag和IterTag接口),它是专门管理jsp修改功能的,多了两个量“EVAL_BODY_BUFFERED”和“EVAL_BODY_TAG(过时的)”,和两个方法setBodyContent()和doInitBody()。



首先让doStartTag()方法返回“EVAL_BODY_BUFFERED”,服务器拿到标签体之后,会将标签体使用setBodyContent()方法传给标签处理器类。我们就可以在doEndTag()方法中拿到标签中的内容,修改之后再输出。



我们先来创建这个标签类:


【自定义标签开发】03-自定义标签功能概述_jsp页面_12



重写doStartTag()方法和doEndTag()方法:


package org.zyg.web.tag;

import java.io.IOException;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.BodyContent;
import javax.servlet.jsp.tagext.BodyTag;
import javax.servlet.jsp.tagext.BodyTagSupport;
import javax.servlet.jsp.tagext.Tag;


//修改标签体(把标签体改为大写)
public class UpperTag extends BodyTagSupport {
	
	@Override
	public int doStartTag() throws JspException {
		
		return BodyTag.EVAL_BODY_BUFFERED;
	}
	
	@Override
	public int doEndTag() throws JspException {
		BodyContent bc = this.getBodyContent();  //得到标签体对象
		String content = bc.getString();//得到标签体内容
		content = content.toUpperCase();//将标签体内容变为大写
		
		try {
			this.pageContext.getOut().write(content);
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
		
		return Tag.EVAL_PAGE;
	}
}


然后在zyg.tld文件中注册一下这个标签:


<tag>
    <name>upper</name><!-- 标签名 -->
    <tag-class>org.zyg.web.tag.UpperTag</tag-class>
    <body-content>JSP</body-content><!-- 有无标签体(单标签还是成对标签) -->
</tag>


然后我们重启一下Web应用,测试:


【自定义标签开发】03-自定义标签功能概述_jsp页面_13


我们的小写英文变成了大写。



我们讲解完了自定义标签的内容,但是,回顾一下这张图:


【自定义标签开发】03-自定义标签功能概述_html_14


我们所讲述的是左边的技术,它其实是jsp2.0以前的技术,后来sun公司觉得Tag的学习成本比较高,不利于标签开发技术的推广,于是在jsp2.0的时候他们又定义了SimpleTag接口,单单使用这一个接口,就可以完成左边3个接口完成的事情。



我们现在公司使用的很多框架还是jsp2.0之前的技术,所以我们学习老技术还是很有必要的,这样就会理解老标签的源码。



jsp2.0以前的标签叫“传统标签”,以后的叫“简单标签”


下一次我们就讲解新的“简单标签”。