jsp中自定义Taglib基础篇(1)
一、自定义标签入门之无参数自定义标签
1.1、自定义标签简介
当我们在JSP页面使用一个简单的标签时,底层实际上由标签处理类提供支持,从而可以使用简单的标签来封装复杂的功能,从而使团队更好地协作开发。
自定义标签顶级接口:jspTag接口,它有两个子接口Tag接口和simpleTage接口(jsp2.0中);
常用自定义标签类继承:javax.servlet.jsp.tagext.SimpleTagSupport,或者TagSupport类。
重写doTag()或者doStartTag()或doEndTag()方法方法,这个方法负责生成页面内容。
1.2、首先介绍是不带属性的标签以HelloWorld为例:
Java代码如下:
public class HelloWorldTag extends TagSupport { private static final long serialVersionUID = -3382691015235241708L; @Override public int doStartTag() { try { pageContext.getOut().write("Hello World"); return super.doStartTag(); } catch (JspException e) { e.printStackTrace(); return 0; } catch (IOException e) { e.printStackTrace(); return 0; } } }
注意:
问题1:tagsupport中的dostartTag和doEndTag这两个方法有什么区别
doStartTag是在扫描到起始标签时调用,doEndTag是在扫描到结束标签是调用。
例如:<helloWorld> helloWorld</helloWorld>
则jsp引擎分析到<helloWorld> 时调用doStratTag, 分析到</helloWorld>时调用doEndTag
1.3、建立TLD文件
TLD是Tag Library Definition的缩写,即标签库定义,文件的后缀是tld,每个TLD文件对应一个标签库,一个标签库中可包含多个标签,TLD文件也称为标签库定义文件。
标签库定义文件的根元素是taglib,它可以包含多个tag子元素,每个tag子元素都定义一个标签。通常我们可以到Web容器下复制一个标签库定义文件,并在此基础上进行修改即可。例如Tomcat6.0,在webapps\examples\WEB-INF\jsp2路径下包含了一个jsp2-example-taglib.tld文件,这就是示范用的标签库定义文件。
将该文件复制到Web应用的WEB-INF/路径,或WEB-INF的任意子路径下,并对该文件进行简单修改,修改后的helloworld.tld文件代码如下:
<?xml version="1.0" encoding="UTF-8"?> <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 web-jsptaglibrary_2_0.xsd" version="2.0"> <tlib-version>1.0</tlib-version> <short-name>myhelloworld</short-name> <!-- 定义该标签库的URI 必须添加但可以空--> <uri>taglib-info</uri> <!-- 定义第一个标签 --> <tag> <!-- 定义标签名 --> <name>helloWorld</name> <!-- 定义标签处理类 --> <tag-class>org.lxh.taglib.HelloWorldTag</tag-class> <!-- 定义标签体为空 --> <body-content>empty</body-content> </tag> </taglib>
问题1:为什么要用TagSupport与BodyTagSupport的区别主要是标签处理类是否需要与标签体交互,如果不需要交互的就用TagSupport,否则就用BodyTagSupport。
交互就是标签处理类是否要读取标签体的内容和改变标签体返回的内容。用TagSupport实现的标签,都可以用BodyTagSupport来实现,因为BodyTagSupport继承了TagSupport而不去实现IterationTag接口的,因为BodyTagSupport继承了TagSupport类,并且该类已经实现了IterationTag接口并且实现了功能.
doStartTag()方法在标签开始时执行,要记住每次都要对类进行初始化,避免上一次的遗留数据对操作造成影响。然后判断是否有数据需要处理,如果有,则返回EVAL_BODY_INCLUDE开始处理标签里的内容,如果没有,返回 EVAL_PAGE跳过标签内容执行标签下面的内容。
doAfterBody()方法在每次处理完标签内部内容后执行,判断循环是否已经结束,如果可以继续循环,返回EVAL_BODY_AGAIN用循环得到新的数据再次处理标签内部内容,如果循环结束就返回EVAL_PAGE结束标签。
1.4、编写jsp页面
<%@taglib uri="taglib-info" prefix="tm" %>
<tm:helloWorld/>
二、自定义JSP标签的处理过程:
1.在JSP中引入标签库:
2.在JSP中使用标签库标签
3.Web容器根据第二个步骤中的prefix,获得第一个步骤中声明的taglib的uri属性值
4.Web容器根据uri属性在web.xml找到对应的元素
5.从元素中获得对应的元素的值
6.Web容器根据元素的值从WEB-INF/目录下找到对应的.tld文件
7.从.tld文件中找到与tagname对应的元素
8.凑元素中获得对应的元素的值
9.Web容器根据元素的值创建相应的tag handle class的实例
10. Web容器调用这个实例的doStartTag/doEndTag方法完成相应的处理
三、创建和使用一个Tag Library的基本步骤:
1.创建标签的处理类(Tag Handler Class)
2.创建标签库描述文件(Tag Library Descrptor File)
3.在JSP文件中引人标签库
四、TagSupport类简介:
1.处理标签的类必须扩展javax.servlet.jsp.TagSupport.
2.TagSupport类的主要属性:
A.parent属性:代表嵌套了当前标签的上层标签的处理类
B.pageContex属性:代表Web应用中的javax.servlet.jsp.PageContext对象
3.JSP容器在调用doStartTag或者doEndTag方法前,会先调用setPageContext和setParent方法,设置pageContext和parent。因此在标签处理类中可以直接访问pageContext变量
4.在TagSupport的构造方法中不能访问pageContext成员变量,因为此时JSP容器还没有调用setPageContext方法对pageContext进行初始化
五、TagSupport处理标签的方法:
1.TagSupport类提供了两个处理标签的方法:
public int doStartTag() throws JspException
public int doEndTag() throws JspException
2.doStartTag:但JSP容器遇到自定义标签的起始标志,就会调用doStartTag()方法,doStartTag()方法返回一个整数值,用来决定程序的后续流程。
A.Tag.SKIP_BODY:表示跳过了开始和结束标签之间的代码
B.Tag.EVAL_BODY_INCLUDE:表示标签之间的内容被正常执行
C.Tag.EVAL_BODY_BUFFERED :对包含的内容进行解析
3.doEndTag:但JSP容器遇到自定义标签的结束标志,就会调用doEndTag()方法。doEndTag()方法也返回一个整数值,用来决定程序后续流程。
A.Tag.SKIP_PAGE:表示立刻停止执行网页,网页上未处理的静态内容和JSP程序均被忽略任何已有的输出内容立刻返回到客户的浏览器上。
B.Tag.EVAL_PAGE:表示按照正常的流程继续执行JSP网页
4.doAfterTag:遇到标签体执行
A.Tag.EVAL_BODY_AGAIN;// 如果集合中还有对像,则循环执行标签体,对标签体循环处理,(存在于javax.servlet.jsp.tagext.IterationTag接口中)
B.Tag.SKIP_BODY
六、创建含有字段的标签:
1.创建标签处理器类FieldTag
package com.able.tag; import java.io.IOException; import javax.servlet.jsp.JspException; import javax.servlet.jsp.JspWriter; import javax.servlet.jsp.tagext.TagSupport; public class FieldTag extends TagSupport { private static final long serialVersionUID = 1540529069962423355L; private String field; private Integer count; @Override public int doEndTag() throws JspException { try { JspWriter out = pageContext.getOut(); out.print(field); out.print(count); } catch (IOException e) { e.printStackTrace(); } return super.doEndTag(); } public String getField() { return field; } public void setField(String field) { this.field = field; } public Integer getCount() { return count; } public void setCount(Integer count) { this.count = count; } }
2.在tag.tld文件中天剑tag标签
<tag> <!-- 定义标签名 --> <name>field</name> <!-- 定义标签处理类 --> <tag-class>com.able.tag.FieldTag</tag-class> <!-- 定义标签体为空 --> <body-content>empty</body-content> <attribute> <name>field</name> <required>true</required> <!-- 是否必須赋值 --> <rtexprvalue>true</rtexprvalue><!-- 表示是否接受jsp语法或者el语言或其他动态语言,默认false --> </attribute> <attribute> <name>count</name> <rtexprvalue>true</rtexprvalue> </attribute> </tag>
3.jsp中定义标签:
<tm:field field="11" count="22"/>
七、如何创建标签处理类
1、引入必需的资源
import javax.servlet.jsp.*; import javax.servlet.http.*; import java.util.*; import java.io.*;
2、继承TagSupport类并覆盖doStartTag()/doEndTag()方法
3、从ServletContext对象中获取java.util.Properties对象
4、从Properties对象中获取key对应的属性值
5、对获取的属性进行相应的处理并输出结果
创建标签库描述文件(Tag Library Descriptor)
1、标签库描述文件,简称TLD,采用XML文件格式,定义了用户的标签库。TLD文件中的元素可以分成3类:
A.标签库元素
B.标签元素
C.标签属性元素
2、标签库元素用来设定标签库的相关信息,它的常用属性有:
A.shortname:指定Tag Library默认的前缀名(prefix);
B.uri:设定Tag Library的惟一访问表示符。
3、标签元素用来定义一个标签,它的常见属性有:
A.name:设定Tag的名字;
B.tagclass:设定Tag的处理类;
C.bodycontent:设定标签的主体(body)内容。
1)empty:表示标签中没有body;
2)JSP:表示标签的body中可以加入JSP程序代码;
3)tagdependent:表示标签中的内容由标签自己去处理。
4、标签属性元素用来定义标签的属性,它的常见属性有:
A.name:属性名称;
B.required:属性是否必需的,默认为false;
C.rtexprvalue:属性值是否可以为request-time表达式,也就是类似于< %=…% >的表达式。
八、在Web应用中使用标签
1、如果Web应用中用到了自定义JSP标签,则必须在web.xml文件中加入元素,它用于声明所引用的标签所在的标签库
/sometaglib
/WEB-INF/someTLD.tld
2、设定Tag Library的惟一标示符,在Web应用中将根据它来引用Tag Libray;
3、指定和Tag Library对应的TLD文件的位置;
4、在JSP文件中需要加入<!-- taglib% >指令来声明对标签库的引用。
5、prefix表示在JSP网页中引用这个标签库的标签时的前缀,uri用来指定Tag Library的标识符,它必须和web.xml中的属性保持一致。
九、案例:
4.1.创建标签描述符文件
在WEB-INF文件下创建*.tld标签描述符文件:如
<taglib>
<tlibversion>1.0</tlibversion>
<jspversion>1.1</jspversion>
<shortname>eRedLab JSPTag Library</shortname>
<uri>/testTag</uri>
<info>自定义标签测试</info>
<tag>
<name>hello</name>
<tagclass>com.eredlab.taglib.test.TestTld</tagclass>
<bodycontent>empty</bodycontent>
<info>自定义标签测试</info>
<attribute>
<name>begin</name>
<required>true</required>
</attribute>
<attribute>
<name>end</name>
<required>true</required>
</attribute>
</tag>
</taglib>
4.2.创建标签处理器
/**
* @desc 自定义标签测试类 实现一个简单的Hello World标签
* @author 夏中伟
* @version eRedLab 2007-9-10
*/
public class TestTld extends TagSupport{
//标签属性begin
private String begin = null;
//标签属性end
private String end = null;
//构造函数
public TestTld(){
}
/* 标签初始方法 */
public int doStartTag() throws JspTagException{
return super.EVAL_BODY_INCLUDE;
}
/* 标签结束方法 */
public int doEndTag() throws JspTagException{
JspWriter out = pageContext.getOut();
String sum = begin + end;
try{
//标签的返回值
out.println(sum);
}catch(IOException e){
e.printStackTrace();
}
return super.SKIP_BODY;
}
/* 释放资源 */
public void release(){
super.release();
}
/********************************************
属性get()、set()方法
*******************************************/
}
在Web.XML中加载标签描述符文件.
<!-- 加载标签描述符文件 -->
<taglib>
<taglib-uri>/WEB-INF/test.tld</taglib-uri>
<taglib-location>/WEB-INF/test.tld</taglib-location>
</taglib>
5.2.在JSP中使用此标签
<%@ taglib uri="/testTag" prefix="mytag"%>
<mytag:hello end="夏中伟!" begin="自定义标签输出流:Hello,"/>
<mytag:hello end="World!" begin="Hi,"/>
WEB页面输出结果如下:
自定义标签输出流:Hello,夏中伟! Hi,World!
循环标签体类:ForEach.java 1import java.util.Collection; 2import java.util.Iterator; 3 4import javax.servlet.jsp.JspException; 5import javax.servlet.jsp.tagext.BodyContent; 6import javax.servlet.jsp.tagext.BodyTagSupport; 7 8public class ForEach extends BodyTagSupport 9{ 10 private String id; 11 private String collection; 12 private Iterator iter; 13 14 public void setCollection(String collection) 15 { 16 this.collection = collection; 17 } 18 public void setId(String id) 19 { 20 this.id = id; 21 } 22 23 //遇到开始标签执行 24 public int doStartTag() throws JspException 25 { 26 Collection coll = (Collection) pageContext.findAttribute(collection); 27 // 表示如果未找到指定集合,则不用处理标签体,直接调用doEndTag()方法。 28 if(coll==null||coll.isEmpty()) return SKIP_BODY; 29 30 iter = coll.iterator(); 31 pageContext.setAttribute(id, iter.next()); 32 // 表示在现有的输出流对象中处理标签体,但绕过setBodyContent()和doInitBody()方法 33 // 这里一定要返回EVAL_BODY_INCLUDE,否则标签体的内容不会在网页上输出显示 34 return EVAL_BODY_INCLUDE; 35 } 36 37 //在doInitBody方法之前执行,在这里被绕过不执行 38 @Override 39 public void setBodyContent(BodyContent arg0) 40 { 41 System.out.println("setBodyContent"); 42 super.setBodyContent(arg0); 43 } 44 //此方法被绕过不会被执行 45 @Override 46 public void doInitBody() throws JspException 47 { 48 System.out.println("doInitBody"); 49 super.doInitBody(); 50 } 51 52 //遇到标签体执行 53 public int doAfterBody() throws JspException 54 { 55 if(iter.hasNext()) 56 { 57 pageContext.setAttribute(id, iter.next()); 58 return EVAL_BODY_AGAIN;// 如果集合中还有对像,则循环执行标签体 59 } 60 return SKIP_BODY;//迭代完集合后,跳过标签体,调用doEndTag()方法。 61 } 62 63 //遇到结束标签执行 64 public int doEndTag() throws JspException 65 { 66 System.out.println("doEndTag"); 67 return EVAL_PAGE; 68 } 69 70} 获取VO属性类:GetProperty.java 1import java.lang.reflect.Method; 2 3import javax.servlet.jsp.JspException; 4import javax.servlet.jsp.tagext.BodyTagSupport; 5 6public class GetProperty extends BodyTagSupport 7{ 8 9 private String name; 10 private String property; 11 12 public void setName(String name) 13 { 14 this.name = name; 15 } 16 17 public void setProperty(String property) 18 { 19 this.property = property; 20 } 21 22 @SuppressWarnings("unchecked") 23 public int doStartTag() throws JspException 24 { 25 try 26 { 27 Object obj = pageContext.findAttribute(name); 28 29 if (obj == null) return SKIP_BODY; 30 31 Class c = obj.getClass(); 32 //构造GET方法名字 get+属性名(属性名第一个字母大写) 33 String getMethodName = "get" + property.substring(0, 1).toUpperCase() 34 + property.substring(1, property.length()); 35 Method getMethod = c.getMethod(getMethodName, new Class[]{}); 36 37 pageContext.getOut().print(getMethod.invoke(obj)); 38 System.out.print(property + ":" + getMethod.invoke(obj) + "t"); 39 } catch (Exception e) 40 { 41 e.printStackTrace(); 42 } 43 return SKIP_BODY; 44 } 45 46 public int doEndTag() throws JspException 47 { 48 return EVAL_PAGE; 49 } 50} 51 52表达式直接访问此类中静态的方法:ELFunction.java 53public class ELFunction 54{ 55 public static int add( int i,int j ) 56 { 57 return i+j; 58 } 59} 写一个测试用的VO类:UserVo.java 1public class UserVo 2{ 3 private String name; 4 private String password; 5 6 public String getName() 7 { 8 return name; 9 } 10 public void setName(String name) 11 { 12 this.name = name; 13 } 14 public String getPassword() 15 { 16 return password; 17 } 18 public void setPassword(String password) 19 { 20 this.password = password; 21 } 22} 建好TLD文件tag.tld,放在WEB-INF目录下 1<?xml version="1.0" encoding="utf-8"?> 2<taglib version="2.0" 3 xmlns="http://java.sun.com/xml/ns/j2ee" 4 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 5 xmlns:shcemalocation="http://java.sun.com/xml/ns/j2ee 6 http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> 7 8 <description>自定义标签</description> 9 <display-name>JSTL core</display-name> 10 <tlib-version>1.1</tlib-version> 11 <short-name>firstLabel</short-name> 12 <uri>http://java.sun.com/jsp/jstl/core</uri> 13 14 <!-- 创建自定义 迭代标签 --> 15 <tag> 16 <name>forEach</name> 17 <tag-class>exercise.taglib.ForEach</tag-class> 18 <!-- 如果没有标签体,设置empty , 如果有标签休必须设置JSP--> 19 <body-content>JSP</body-content> 20 <attribute> 21 <name>id</name> 22 <required>true</required><!-- 标识属性是否是必须的 --> 23 <rtexprvalue>true</rtexprvalue><!-- 标识属性值是否可以用表达式语言 --> 24 </attribute> 25 <attribute> 26 <name>collection</name> 27 <required>true</required> 28 <rtexprvalue>true</rtexprvalue> 29 </attribute> 30 </tag> 31 32 <!-- 创建自定义获得属性标签 --> 33 <tag> 34 <name>getProperty</name> 35 <tag-class>exercise.taglib.GetProperty</tag-class> 36 <body-content>empty</body-content> 37 <attribute> 38 <name>name</name> 39 <required>true</required> 40 <rtexprvalue>true</rtexprvalue> 41 </attribute> 42 <attribute> 43 <name>property</name> 44 <required>true</required> 45 <rtexprvalue>true</rtexprvalue> 46 </attribute> 47 </tag> 48 49 <!-- 配置一个表达式调用 的函数 --> 50 <function> 51 <name>add</name><!-- 配置一个标签,在JSP页面通过引用前缀调用 --> 52 <function-class>exercise.taglib.ELFunction</function-class><!-- 实现类 --> 53 <function-signature>int add(int,int)</function-signature><!-- 静态的方法:包括返回类型,方法名,入参的类型 --> 54 </function> 55</taglib>
在web.xml文件中配置自定义标签
1<jsp-config> 2 <taglib> 3 <taglib-uri>firstTag</taglib-uri> 4 <taglib-location>/WEB-INF/tag.tld</taglib-location> 5 </taglib> 6</jsp-config> 在jsp文件中使用标签:tag.jsp 1<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%> 2<%@ taglib uri="firstTag" prefix="my"%> 3 4<jsp:useBean id="userVo1" class="exercise.vo.UserVo" scope="request"> 5 <jsp:setProperty name="userVo1" property="name" value="Hackiller"/> 6 <jsp:setProperty name="userVo1" property="password" value="123"/> 7</jsp:useBean> 8 9<jsp:useBean id="userVo2" class="exercise.vo.UserVo" scope="request"> 10 <jsp:setProperty name="userVo2" property="name" value="YangYang"/> 11 <jsp:setProperty name="userVo2" property="password" value="456"/> 12</jsp:useBean> 13 14<% 15 List list = new ArrayList(); 16 list.add(userVo1); 17 list.add(userVo2); 18 pageContext.setAttribute("voList",list); 19%> 20 21<html> 22 <head> 23 <title>My JSP 'tag.jsp' starting page</title> 24 </head> 25 26 <body> 27 <h2 align="center">This is my JSP page:测试taglib.</h2> 28 <hr> 29 30 <h2>自定义迭代标签:</h2> 31 <table> 32 <tr><td>姓名</td><td>密码</td></tr> 33 <my:forEach collection="voList" id="uservo"> 34 <tr> 35 <td><my:getProperty name="uservo" property="name"/></td> 36 <td><my:getProperty name="uservo" property="password"/></td> 37 </tr> 38 </my:forEach> 39 </table> 40 <hr> 41 42 <h2>表达式调用类的静态方法:</h2> 43 2+5=${my:add(2,5)} 44 </body> 45</html>