简介
层次结构
首先我们需要大致了解开发自定义标签所涉及到的接口与类的层次结构
引入类包
import javax.servlet.jsp.*;
import javax.servlet.jsp.tagext.*;
javax.servlet.jsp.*中包括了类JspException,通过这个类完成对JSP的异常处理。而javax.servlet.jsp.tagext.*,如果是SimpleTag,一般是继承里面的SimpleTagSupport类完成自定义标签的开发过程;如果是最初的tag自定义标签,则需要继承里面的BodyTagSupport类完成开发。
开发步骤
(1)创建定义标签类:类成员属性为private;方法属性为public;对于重写的重要方法,如doTag(),前面加上@Override来提示。
(2)在WEB-INF文件夹下面创建.tld文件,这样web容器会自动加载它。
(3)配置web.xml,主要是在里面加上<taglib></taglib>完成对自己自定义标签的设置。
(4)在需要使用此标签的jsp页面头部引入<%@ taglib uri="/mytaglib" prefix="cc"%>,然后使用即可。
SimpleTag自定义标签
实例详解
我们创建一个自定义的if标签,按照开发步骤:创建自定义标签类IfTag.java
package cc.openhome.tag;
import java.io.IOException;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.SimpleTagSupport;
public class IfTag extends SimpleTagSupport{//继承SimpleTagSupport类
private boolean test;
public void setTest(boolean test){//创建设值方法
this.test=test;
}
@Override
public void doTag() throws JspException,IOException{//重新定义doTag()方法
if(test){
getJspBody().invoke(null);//通过getJspBody取得JspFragment,然后调用invoke
}
}
}
在WEB-INF下创建f.tld文件
<?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"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
web-jsptaglibrary_2_0.xsd">
<tlib-version>1.0</tlib-version><!--taglib版本号-->
<!--<%@taglib中的prefix的值对应short-name,uri和这里定义的uri相同,不定义uri会报错-->
<short-name>f</short-name>
<uri>http://openhome.cc/jstl/fake</uri>
<tag>
<name>if</name><!--tag的名字-->
<tag-class>cc.openhome.tag.IfTag</tag-class><!--tag对应的java类的名字-->
<body-content>scriptless</body-content>
<attribute><!--设置标签上的属性-->
<name>test</name>
<required>true</required><!--一定需要这个选项-->
<rtexprvalue>true</rtexprvalue><!--表示这个参数是否接受动态赋值-->
<type>boolean</type><!--这个属性的类型-->
</attribute>
</tag>
</taglib>
配置web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:jsp="http://java.sun.com/xml/ns/javaee/jsp" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0">
<context-param>
<param-name>USERS</param-name>
<param-value>c:/workspace/SimpleTagDemo/users</param-value>
</context-param>
<jsp-config>
<jsp-property-group>
<url-pattern>*.jsp</url-pattern>
<page-encoding>UTF-8</page-encoding>
<default-content-type>text/html</default-content-type>
</jsp-property-group>
</jsp-config>
<!--自己添加的关于taglib内容-->
<jsp-config>
<taglib>
<taglib-uri>http://openhome.cc/jstl/fake</taglib-uri><!--和tld中的uri相同-->
<taglib-location>/WEB-INF/f.tld</taglib-location><!--*.tld文件的位置-->
</taglib>
</jsp-config>
</web-app>
jsp中使用标签
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<!-- 注意和tld文件中的对应关系 -->
<%@taglib prefix="f" uri="http://openhome.cc/jstl/fake" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>自定义标签if</title>
</head>
<body>
<f:if test="true"}">your secret is here</f:if>
</body>
</html>
重点说明
(1)tld文件的<tag></tag>中,有个<body-content>,属性为scriptless时候,表示不能出现Scriptlet;empty表示一定没有Body内容;tagdependent表示将Body中的内容当做纯文字输出,不会做任何的运算和转译;
(2)tld文件的<tag></tag>中,如果设置为true表示接受动态赋值;如果为false或者默认,表示jsp设置属性时仅接受字符串形式。
(3)jsp页面头部引入<%@ taglib uri="/mytaglib" prefix="cc"%>的时候,这个uri必须同时在tld和web.xml中说明,否则程序报错。另外说一点,在使用JSTL时候,如<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core" %>通常会报错,这往往是缺乏jstl.jar和standard.jar包造成,这个时候需要把这两个包放到WEB-INF的lib文件夹下面,如果还是报错,这个时候需要在web.xml中添加<taglib></taglib>进行相应的说明;
(4)在web.xml中写<taglib>会出现红色的报错。出现这种错误的原因是所使用的版本的问题,如果使用的是2.3的版本是可以直接在<web-app>里面书写,例如:但是如果使用的是2.4版本的,那么这个标签就不能直接在<web-app>中使用了,这个时候就必须用<jsp-config></jsp-config>把<taglib></taglib>包含住,我所使用的就是2.4版本。
(5)在自定义标签类中使用到了invoke()方法。JspFragment.invoke方法可以说是JspFragment最重要的方法,利用这个方法可以控制是否执行和输出标签体的内容、是否迭代执行标签体的内容或对标签体的执行结果进行修改后再输出。例如:
在标签处理器中如果没有调用JspFragment.invoke方法,其结果就相当于忽略标签体内容;
在标签处理器中重复调用JspFragment.invoke方法,则标签体内容将会被重复执行;
若想在标签处理器中修改标签体内容,只需在调用invoke方法时指定一个可取出结果数据的输出流对象(例如StringWriter),让标签体的执行结果输出到该输出流对象中,然后从该输出流对象中取出数据进行修改后再输出到目标设备,即可达到修改标签体的目的。
API架构
JSP中会按照以下步骤处理SimpleTag:
(1)创建自定义标签处理器实例;
(2)调用标签处理器的setJspContext()方法设置pageContext实例;
(3)如果是嵌套标签中的内层标签,则还会调用标签处理器的setParent()方法,并传入外层标签处理器的实例;
(4)设置标签处理器属性(实例中是调用setText()方法来设置)
(5)调用标签处理器的setJspBody()方法来设置JspFragment实例;
(6)调用标签处理器的doTag()方法。
(7)销毁标签处理器实例。
每一次请求都会创建新的标签处理器实例,而在执行doTag()方法之后就销毁实例,在该标签的实现中,尽量不要有耗资源的操作。
内外层标签的交互
在SimpleTagSupport类中有个JspTag getParent()方法,因此在doTag()方法中,内层标签往往通过JspTag parent=getParent()来取得上一层的标签;如果在数个嵌套的标签中,想要直接取得某个指定类型的外层标签,则可以通过SimpleTagSupport的findAncestorWithClass()方法,如:SomeTag ancetor=(SomeTag) findAncesrouWithClass(this,SomeTag.class),该方法会在目前标签的外层标签中寻找,直到找到指定的类型对象。
Tag自定义标签
标签处理流程
使用TagSupport实现标签的流程
setPageContext() 将所在jsp页面的pageContext注入进来,目的是为了在后面的方法中可以访问到jsp页面对象的pageContext属性
setParent() 设置此标签的父标签
setAttribute() 将标签中的属性注入到此class的属性,不需要自己实现但要提供属性的get与set方法
doStartTag() 在开始标签属性设置后调用,如果返回SKIP_BODY则忽略标签之中的内容,如果返回EVAL_BODY_INCLUDE则将标签体的内容进行输出
doAfterBody() 如果返回EVAL_BODY_AGAIN,那么将继续执行Body体一次
doEndTag() 在结束标签之前调用,返回SKIP_PAGE跳过整个jsp页面后面的输出,返回EVAL_PAGE执行页面余下部分
release() 生命周期结束时调用
使用BodyTagSupport实现标签的流程
TagSupport与BodyTagSupport的区别
TagSupport与BodyTagSupport的区别主要是标签处理类是否需要与标签体交互(标签处理类是否要读取标签体的内容和改变标签体返回的内容),如果不需要交互的就用TagSupport,否则如果需要交互就用BodyTagSupport。用TagSupport实现的标签,都可以用BodyTagSupport来实现,因为BodyTagSupport继承了TagSupport。 TagSupport与BodyTagSupport的区别主要是标签处理类是否需要与标签体交互(标签处理类是否要读取标签体的内容和改变标签体返回的内容),如果不需要交互的就用TagSupport,否则如果需要交互就用BodyTagSupport。
用TagSupport实现的标签,都可以用BodyTagSupport来实现,因为BodyTagSupport继承了TagSupport。 TagSupport与BodyTagSupport的区别主要是标签处理类是否需要与标签体交互(标签处理类是否要读取标签体的内容和改变标签体返回的内容),如果不需要交互的就用TagSupport,否则如果需要交互就用BodyTagSupport。
用TagSupport实现的标签,都可以用BodyTagSupport来实现,因为BodyTagSupport继承了TagSupport。
实例详解
实例1:开发一个ForEach标签来实现循环处理。
开发标签处理器类
package cc.openhome.tag;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.TagSupport;
import java.util.Collection;
import java.util.Iterator;
public class ForEachTag extends TagSupport {
private String var;
private Iterator iterator;
public void setVar(String var){
this.var=var;
}
public void setItems(Collection items){
this.iterator=items.iterator();
}
@Override
public int doStartTag() throws JspException{
if(iterator.hasNext()){
this.pageContext.setAttribute(var,iterator.next());//测试并执行第一次处理
return EVAL_BODY_INCLUDE;
}
return SKIP_BODY;
}
@Override
public int doAfterBody()throws JspException{
if(iterator.hasNext()){
this.pageContext.setAttribute(var,iterator.next());
return EVAL_BODY_AGAIN;
}
return SKIP_BODY;
}
}
设置.tld文件
就在上面的f.tld文件中加入以下语句即可
<tag>
<name>forEach</name>
<tag-class>cc.openhome.tag.ForEachTag</tag-class>
<body-content>JSP</body-content>
<attribute>
<name>var</name>
<required>true</required>
<type>java.lang.String</type>
</attribute>
<attribute>
<name>items</name>
<required>true</required>
<rtexprvalue>true</rtexprvlaue>
<type>java.util.Collection</type>
</attribute>
</tag>
web.xml里面内容已经进行配置,无需再修改,接着就可以使用标签了。
注意
在使用.tld配置文件里面的内容<body-content>里面的设置为JSP,在Tag自定义标签的tld文件中,这部分内容可以有empty、JSP、tagdependent。JSP的设置值表示Body中若包括动态内容,如Script等元素、EL或自定义标签都会被执行。
实例2:Tag实现自定义if标签
上面已经用SimpleTag实现了If标签,现在改为Tag实现,标签处理器类如下:
package cc.openhome.tag;
import java.io.IOException;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.TagSupport;
public class IfTag extends TagSupport{//继承TagSupport类
private boolean test;
public void setTest(boolean test){//创建设值方法
this.test=test;
}
@Override
public int doStartTag() throws JspException{
if(test){
return EVAL_BODY_INCLUDE;
}
return SKIP_BODY;//执行失败,忽略Body内容
}
}
自定义函数库
创建函数库类
public class MyFunctions {
public static String formatMyName(String name) {
return "your name is " + name;
}
public static int add(int a, int b) {
return a+b;
}
}
配置tld文件
<function>
<name>formatMyName</name>
<function-class>com.taglib.MyFunctions</function-class>
<function-signature>java.lang.String formatMyName(java.lang.String)</function-signature>
</function>
<function>
<name>add</name>
<function-class>com.taglib.MyFunctions</function-class>
<function-signature>java.lang.String add(int, int)</function-signature>
</function>
在JSP中使用。
关于自定义标签就说这么多,参考http://www.cnblogs.com/zhaoyang/archive/2011/12/25/2301108.html