jsp自定义标签

一、JSP自定义标签简介
标签是一种XML元素,通过标签可以使JSP网页变得简洁并且易于维护,还可以方便地实现同一个JSP文件支持多种语言版本。由于标签是XML元素,所以它的名称和属性都是大小写敏感的
标准JSP标签是用来调用JavaBean组件的操作,处理定向请求以简化JSP页面开发与维护。JSP技术提供了一种封装其它动态类型的机制——自定义标签,它扩展了JSP语言。自定义标签通常发布在标签库中,该库定义了一个自定义标签集并包含实现标签的对象。
自定义标签是用户定义的JSP语言元素。当JSP页面包含一个自定义标签时被转化为servlet,标签转化为对称为tag handler的对象的操作。接着当servlet执行时Web container调用那些操作。



二、两种标签
可以定义两种类型的标签:
javax.servlet.jsp.tagext.Tag
javax.servlet.jsp.tagext.BodyTag

有标签体的标签必须实现 BodyTag 接口。

Html代码
<jsptag:map scope=“session” name=“tagMap”> 
  body  
</jsptag:map> 

<jsptag:map scope=“session” name=“tagMap”>
  body
</jsptag:map>
也可能没有标签体:

Html代码
<jsptag:map/> 

<jsptag:map/> 无标签体的简单标签可以实现 Tag 接口。



三、标签处理程序
int doStartTag() throws JspException---处理开始标签
int doEndTag() throws JspException---处理结束标签
Tag getParent()/void setParent(Tag t)---获得/设置标签的父标签
void setPageContext(PageContext pc)--- pageContext 属性的 setter 方法
void release() 释放获得的所有资源



doStartTag()和doEndTag()方法的返回值说明:
SKIP_BODY      表示不用处理标签体,直接调用doEndTag()方法。
SKIP_PAGE      忽略标签后面的jsp(SUN企业级应用的首选)内容。
EVAL_PAGE      处理标签后,继续处理jsp(SUN企业级应用的首选)后面的内容。
EVAL_BODY_BUFFERED 表示需要处理标签体,且需要重新创建一个缓冲(调用setBodyContent方法)。
EVAL_BODY_INCLUDE  表示在现有的输出流对象中处理标签体,但绕过setBodyContent()和doInitBody()方法
EVAL_BODY_AGAIN     对标签体循环处理。(存在于javax.servlet.jsp.tagext.IterationTag接口中)



实现javax.servlet.jsp.tagext.Tag接口
扩展javax.servlet.jsp.tagext.TagSupport类
TagSupport 类定义了 get/setParent() 和 setPageContext(),这与所有标签处理程序几乎相同。
get/setParent() 方法允许标签嵌套。
TagSupport 类还定义了一个可以被子类使用的 pageContext 实例变量 (protected PageContext pageContext),这个变量是由 setPageContext() 方法设置的。



在创建自定义标签之前,需要创建一个 标签处理程序。标签处理程序是一个执行自定义标签操作的 Java 对象。在使用自定义标签时,要导入一个 标签库 —— 即一组标签/标签处理程序对。通过在 Web 部署描述符中声明库导入它,然后用指令 taglib 将它导入 JSP 页。

Html代码
<%@ taglib uri="firstTag" prefix="my"%> 

<%@ taglib uri="firstTag" prefix="my"%>

如果 JSP 容器在转换时遇到了自定义标签,那么它就检查 标签库描述符(tag library descriptor) (TLD) 文件以查询相应的标签处理程序。TLD 文件对于自定义标签处理程序,就像 Web 部署描述符对于 servlet 一样。

在运行时,JSP 页生成的 servlet 得到对应于这一页面所使用的标签的标签处理程序的一个实例。生成的 servlet 用传递给它的属性初始化标签处理程序。
标签处理程序实现了 生存周期 方法。生成的 servlet 用这些方法通知标签处理程序应当启动、停止或者重复自定义标签操作。生成的 servlet 调用这些生存周期方法执行标签的功能。



四、TLD 文件
TLD 文件的根元素是 taglib。taglib 描述了一个 标签库 —— 即一组标签/标签处理程序对。
因为我们使用的是 JSP 版本 1.2,所以在这个例子中需要 tlib-version 和 short-name 元素。
tlib-version 元素对应于标签库版本。
jsp-version 对应于标签库所依赖的 JSP 技术的版本。
short-name 元素定义了 IDE 和其他开发工具可以使用的标签库的简单名。
taglib 元素包含许多 tag 元素,标签库中每一个标签有一个 tag 元素。

五、编写自定义迭代标签和el表达式调用类的静态方法实例

循环标签体类:ForEach.java

Java代码
import java.util.Collection;  
import java.util.Iterator;  
 
import javax.servlet.jsp.JspException;  
import javax.servlet.jsp.tagext.BodyContent;  
import javax.servlet.jsp.tagext.BodyTagSupport;  
 
public class ForEach  extends BodyTagSupport  
{  
  private String id;  
  private String collection;  
  private Iterator iter;  
    
  public void setCollection(String collection)  
  {  
    this.collection = collection;  
  }  
  public void setId(String id)  
  {  
    this.id = id;  
  }  
    
  //遇到开始标签执行  
  public int doStartTag() throws JspException  
  {  
    Collection coll = (Collection) pageContext.findAttribute(collection);  
    // 表示如果未找到指定集合,则不用处理标签体,直接调用doEndTag()方法。  
    if(coll==null||coll.isEmpty()) return SKIP_BODY;  
      
    iter = coll.iterator();  
    pageContext.setAttribute(id, iter.next());  
    // 表示在现有的输出流对象中处理标签体,但绕过setBodyContent()和doInitBody()方法  
    // 这里一定要返回EVAL_BODY_INCLUDE,否则标签体的内容不会在网页上输出显示  
    return EVAL_BODY_INCLUDE;  
  }  
    
  //在doInitBody方法之前执行,在这里被绕过不执行  
  @Override 
  public void setBodyContent(BodyContent arg0)  
  {  
    System.out.println("setBodyContent...");  
    super.setBodyContent(arg0);  
  }  
  //此方法被绕过不会被执行  
  @Override 
  public void doInitBody() throws JspException  
  {  
    System.out.println("doInitBody...");  
    super.doInitBody();  
  }  
    
  //遇到标签体执行  
  public int doAfterBody() throws JspException  
  {  
    if(iter.hasNext())   
    {  
      pageContext.setAttribute(id, iter.next());  
      return EVAL_BODY_AGAIN;// 如果集合中还有对像,则循环执行标签体  
    }  
    return SKIP_BODY;//迭代完集合后,跳过标签体,调用doEndTag()方法。  
  }  
    
  //遇到结束标签执行  
  public int doEndTag() throws JspException  
  {  
    System.out.println("doEndTag...");  
    return EVAL_PAGE;  
  }  
 


import java.util.Collection;
import java.util.Iterator;

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

public class ForEach  extends BodyTagSupport
{
  private String id;
  private String collection;
  private Iterator iter;
 
  public void setCollection(String collection)
  {
    this.collection = collection;
  }
  public void setId(String id)
  {
    this.id = id;
  }
 
  //遇到开始标签执行
  public int doStartTag() throws JspException
  {
    Collection coll = (Collection) pageContext.findAttribute(collection);
    // 表示如果未找到指定集合,则不用处理标签体,直接调用doEndTag()方法。
    if(coll==null||coll.isEmpty()) return SKIP_BODY;
   
    iter = coll.iterator();
    pageContext.setAttribute(id, iter.next());
    // 表示在现有的输出流对象中处理标签体,但绕过setBodyContent()和doInitBody()方法
    // 这里一定要返回EVAL_BODY_INCLUDE,否则标签体的内容不会在网页上输出显示
    return EVAL_BODY_INCLUDE;
  }
 
  //在doInitBody方法之前执行,在这里被绕过不执行
  @Override
  public void setBodyContent(BodyContent arg0)
  {
    System.out.println("setBodyContent...");
    super.setBodyContent(arg0);
  }
  //此方法被绕过不会被执行
  @Override
  public void doInitBody() throws JspException
  {
    System.out.println("doInitBody...");
    super.doInitBody();
  }
 
  //遇到标签体执行
  public int doAfterBody() throws JspException
  {
    if(iter.hasNext())
    {
      pageContext.setAttribute(id, iter.next());
      return EVAL_BODY_AGAIN;// 如果集合中还有对像,则循环执行标签体
    }
    return SKIP_BODY;//迭代完集合后,跳过标签体,调用doEndTag()方法。
  }
 
  //遇到结束标签执行
  public int doEndTag() throws JspException
  {
    System.out.println("doEndTag...");
    return EVAL_PAGE;
  }

} 获取VO属性类:GetProperty.java

Java代码
import java.lang.reflect.Method;  
 
import javax.servlet.jsp.JspException;  
import javax.servlet.jsp.tagext.BodyTagSupport;  
 
public class GetProperty extends BodyTagSupport  
{  
 
  private String name;  
  private String property;  
 
  public void setName(String name)  
  {  
    this.name = name;  
  }  
 
  public void setProperty(String property)  
  {  
    this.property = property;  
  }  
 
  @SuppressWarnings("unchecked")  
  public int doStartTag() throws JspException  
  {  
    try 
    {  
      Object obj = pageContext.findAttribute(name);  
        
      if (obj == null) return SKIP_BODY;  
        
      Class c = obj.getClass();  
      //构造GET方法名字 get+属性名(属性名第一个字母大写)  
      String getMethodName = "get" + property.substring(0, 1).toUpperCase()   
                              + property.substring(1, property.length());  
      Method getMethod = c.getMethod(getMethodName, new Class[]{});  
        
      pageContext.getOut().print(getMethod.invoke(obj));  
      System.out.print(property + ":" + getMethod.invoke(obj) + "\t");  
    } catch (Exception e)  
    {  
      e.printStackTrace();  
    }  
    return SKIP_BODY;  
  }  
 
  public int doEndTag() throws JspException  
  {  
    return EVAL_PAGE;  
  }  


import java.lang.reflect.Method;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.BodyTagSupport;

public class GetProperty extends BodyTagSupport
{

  private String name;
  private String property;

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

  public void setProperty(String property)
  {
    this.property = property;
  }

  @SuppressWarnings("unchecked")
  public int doStartTag() throws JspException
  {
    try
    {
      Object obj = pageContext.findAttribute(name);
     
      if (obj == null) return SKIP_BODY;
     
      Class c = obj.getClass();
      //构造GET方法名字 get+属性名(属性名第一个字母大写)
      String getMethodName = "get" + property.substring(0, 1).toUpperCase()
                              + property.substring(1, property.length());
      Method getMethod = c.getMethod(getMethodName, new Class[]{});
     
      pageContext.getOut().print(getMethod.invoke(obj));
      System.out.print(property + ":" + getMethod.invoke(obj) + "\t");
    } catch (Exception e)
    {
      e.printStackTrace();
    }
    return SKIP_BODY;
  }

  public int doEndTag() throws JspException
  {
    return EVAL_PAGE;
  }
} 表达式直接访问此类中静态的方法:ELFunction.java

Java代码
public class ELFunction   
{  
public static int add( int i,int j )  
{  
  return i+j;  
}  


public class ELFunction
{
public static int add( int i,int j )
{
  return i+j;
}
}写一个测试用的VO类:UserVo.java

Java代码
public class UserVo  
{  
  private String name;  
  private String password;  
    
  public String getName()  
  {  
    return name;  
  }  
  public void setName(String name)  
  {  
    this.name = name;  
  }  
  public String getPassword()  
  {  
    return password;  
  }  
  public void setPassword(String password)  
  {  
    this.password = password;  
  }  
}  

public class UserVo
{
  private String name;
  private String password;
 
  public String getName()
  {
    return name;
  }
  public void setName(String name)
  {
    this.name = name;
  }
  public String getPassword()
  {
    return password;
  }
  public void setPassword(String password)
  {
    this.password = password;
  }
}  建好TLD文件tag.tld,放在WEB-INF目录下

Xml代码
<?xml version="1.0" encoding="utf-8"?> 
<taglib version="2.0" 
xmlns="http://java.sun.com/xml/ns/j2ee" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xmlns:shcemalocation="http://java.sun.com/xml/ns/j2ee   
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> 
   
<description>自定义标签</description> 
<display-name>JSTL core</display-name> 
<tlib-version>1.1</tlib-version> 
<short-name>firstLabel</short-name> 
<uri>http://java.sun.com/jsp/jstl/core</uri> 
   
<!-- 创建自定义 迭代标签 --> 
<tag> 
  <name>forEach</name> 
  <tag-class>exercise.taglib.ForEach</tag-class> 
  <!-- 如果没有标签体,设置empty , 如果有标签休必须设置JSP--> 
  <body-content>JSP</body-content> 
  <attribute> 
   <name>id</name> 
   <required>true</required><!-- 标识属性是否是必须的 --> 
   <rtexprvalue>true</rtexprvalue><!-- 标识属性值是否可以用表达式语言 --> 
  </attribute> 
  <attribute> 
   <name>collection</name> 
   <required>true</required> 
   <rtexprvalue>true</rtexprvalue> 
  </attribute> 
</tag> 
   
<!-- 创建自定义获得属性标签 --> 
<tag> 
  <name>getProperty</name> 
  <tag-class>exercise.taglib.GetProperty</tag-class> 
  <body-content>empty</body-content> 
  <attribute> 
   <name>name</name> 
   <required>true</required> 
   <rtexprvalue>true</rtexprvalue> 
  </attribute> 
  <attribute> 
   <name>property</name> 
   <required>true</required> 
   <rtexprvalue>true</rtexprvalue> 
  </attribute> 
</tag> 
   
<!-- 配置一个表达式调用 的函数 --> 
    <function> 
     <name>add</name><!-- 配置一个标签,在JSP页面通过引用前缀调用 --> 
     <function-class>exercise.taglib.ELFunction</function-class><!-- 实现类 --> 
     <function-signature>int add(int,int)</function-signature><!-- 静态的方法:包括返回类型,方法名,入参的类型 --> 
    </function> 
</taglib> 

<?xml version="1.0" encoding="utf-8"?>
<taglib version="2.0"
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:shcemalocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">

<description>自定义标签</description>
<display-name>JSTL core</display-name>
<tlib-version>1.1</tlib-version>
<short-name>firstLabel</short-name>
<uri>http://java.sun.com/jsp/jstl/core</uri>

<!-- 创建自定义 迭代标签 -->
<tag>
  <name>forEach</name>
  <tag-class>exercise.taglib.ForEach</tag-class>
  <!-- 如果没有标签体,设置empty , 如果有标签休必须设置JSP-->
  <body-content>JSP</body-content>
  <attribute>
   <name>id</name>
   <required>true</required><!-- 标识属性是否是必须的 -->
   <rtexprvalue>true</rtexprvalue><!-- 标识属性值是否可以用表达式语言 -->
  </attribute>
  <attribute>
   <name>collection</name>
   <required>true</required>
   <rtexprvalue>true</rtexprvalue>
  </attribute>
</tag>

<!-- 创建自定义获得属性标签 -->
<tag>
  <name>getProperty</name>
  <tag-class>exercise.taglib.GetProperty</tag-class>
  <body-content>empty</body-content>
  <attribute>
   <name>name</name>
   <required>true</required>
   <rtexprvalue>true</rtexprvalue>
  </attribute>
  <attribute>
   <name>property</name>
   <required>true</required>
   <rtexprvalue>true</rtexprvalue>
  </attribute>
</tag>

<!-- 配置一个表达式调用 的函数 -->
    <function>
     <name>add</name><!-- 配置一个标签,在JSP页面通过引用前缀调用 -->
     <function-class>exercise.taglib.ELFunction</function-class><!-- 实现类 -->
     <function-signature>int add(int,int)</function-signature><!-- 静态的方法:包括返回类型,方法名,入参的类型 -->
    </function>
</taglib> 在web.xml文件中配置自定义标签

Xml代码
<jsp-config> 
<taglib> 
  <taglib-uri>firstTag</taglib-uri> 
  <taglib-location>/WEB-INF/tag.tld</taglib-location> 
</taglib> 
</jsp-config> 

<jsp-config>
<taglib>
  <taglib-uri>firstTag</taglib-uri>
  <taglib-location>/WEB-INF/tag.tld</taglib-location>
</taglib>
</jsp-config> 在jsp文件中使用标签:tag.jsp

Java代码
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>  
<%@ taglib uri="firstTag" prefix="my"%>  
 
<jsp:useBean id="userVo1" class="exercise.vo.UserVo" scope="request">  
  <jsp:setProperty name="userVo1" property="name" value="Hackiller"/>  
  <jsp:setProperty name="userVo1" property="password" value="123"/>  
</jsp:useBean>  
 
<jsp:useBean id="userVo2" class="exercise.vo.UserVo" scope="request">  
  <jsp:setProperty name="userVo2" property="name" value="YangYang"/>  
  <jsp:setProperty name="userVo2" property="password" value="456"/>  
</jsp:useBean>  
 
<%  
List list = new ArrayList();  
list.add(userVo1);  
list.add(userVo2);  
pageContext.setAttribute("voList",list);  
%>  
 
<html>  
  <head>  
    <title>My JSP 'tag.jsp' starting page</title>  
  </head>  
    
  <body>  
    <h2 align="center">This is my JSP page:测试taglib.</h2>   
    <hr>  
      
   <h2>自定义迭代标签:</h2>  
    <table>  
     <tr><td>姓名</td><td>密码</td></tr>  
     <my:forEach collection="voList" id="uservo">  
      <tr>  
       <td><my:getProperty name="uservo" property="name"/></td>  
       <td><my:getProperty name="uservo" property="password"/></td>  
      </tr>  
     </my:forEach>  
    </table>  
    <hr>  
      
    <h2>表达式调用类的静态方法:</h2>  
    2+5=${my:add(2,5)}   
  </body>  
</html> 

你可能感兴趣的:(jsp,Web,xml,servlet,企业应用)