一、简介
自定义标签的作用主要是用来替换JSP中的Java脚本。属于JSP技术。
二、自定义标签的开发步骤:
1.编写一个实现Tag接口的Java类(标签处理器类)。
2.编写标签库描述符(tld)文件,在tld文件中把标签处理器类进行描述在tld文件放入应用的web-inf文件中。如输入客户端ip地址的例子具体步骤示例如下:
1>定义一个类,实现javax.servlet.jsp.tagext.Tag接口(通过继承Tag接口的实现类也可)。
public class ShowRemoteIpTag extends TagSupport {
public int doStartTag() throws JspException {
String remoteIp = pageContext.getRequest().getRemoteAddr();
try {
pageContext.getOut().print("您的IP是是:"+remoteIp);
} catch (IOException e) {
e.printStackTrace();
}
return super.doStartTag();
}
}
2、在WEB-INF目录下建立一个扩展名为tld的XML文件(XML的内容参考Tomcat中的示例)fk.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 http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"
version="2.0">
<description>my taglib study.</description>
<tlib-version>1.0</tlib-version>
<short-name>fk</short-name>
<uri>http://www.test.com/jsp/fk</uri>
<tag> <!-- 可定义标签如<fk: showRemoteIp /> -->
<description>Show Remote Ip</description>
<name>showRemoteIp</name>
<tag-class>com.fk.tag.ShowRemoteIpTag</tag-class>
<body-content>empty</body-content>
</tag>
<tag>
<name>forEach</name>
<tag-class>com.fk.tag.example.ForEachSimpleTag</tag-class>
<body-content>scriptless</body-content>
<attribute> <!-- 定义标签属性 -->
<name>items</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>var</name>
<required>true</required>
<rtexprvalue>false</rtexprvalue><!-- var属性是否支持el-->
</attribute>
</tag>
<function> <!-- 也可定义在el中使用的函数如:${fk:contains(users,user)} -->
<description>某一List对象是否包含某一Object对象</description>
<name>contains</name>
<function-class>com.fk.util.ListContainsLabel</function-class>
<function-signature>java.lang.Boolean contains(java.util.List,java.lang.Object)</function-signature>
</function>
</taglib>
3、在web.xml中建立tld文件的存放位置和uri的对应关系。
注:此步骤是可选的,前提是tld文件放在了WEB-INF目录下(建议使用此方式)。
<jsp-config>
<taglib>
<taglib-uri>http://www.test.com/jsp/fk</taglib-uri>
<taglib-location>/WEB-INF/fk.tld</taglib-location>
</taglib>
</jsp-config>
三、Tag接口详解(接口中的方法大部分都是容器调用的,继承了JspTag接口)
intdoStartTag():容器调用。遇到开始标签时调用。
返回值可为:
Tag.SKIP_BODY:指示标签主体内容不执行
Tag.EVAL_BODY_INCLUDE:指示标签主体内容执行
intdoEndTag():容器调用。遇到结束标签时调用。
返回值可为:
Tag.SKIP_PAGE:忽略结束标签之后的JSP内容
Tag.EVAL_PAGE:结束标签后的JSP内容还要继续执行
TaggetParent():用户调用。得到父标签。没有父标签返回null
voidrelease():容器调用。释放标签类占用的资源。
voidsetPageContext(PageContextpc):容器调用。传入当前页面的上下文对象
voidsetParent(Tagt):容器调用。传入当前标签的父标签对象。如果有的话。
四、IterationTag接口:增加了重复执行主体内容的方法。(IterationTag接口继承了Tag接口,TagSupport类实现了IterationTag接口)
intdoAfterBody():主体执行后,结束标签执行前,容器调用
返回值:
Tag.SKIP_BODY:指示标签主体内容不执行
IterationTag.EVAL_BODY_AGAIN:再次调用doAfterBody方法
注:成员变量的问题。
五、BodyTag:增加了获取主体内容的方法
voiddoInitBody():容器调用
voidsetBodyContent(BodyContentb):容器调用,传递标签的主体内容
注意:以上2个方法都是由容器调用。要求doStartTag()方法必须返回BodyTag.EVAL_BODY_BUFFERED
六、传统标签返回值图、标签类继承及传统标签的执行流程
七、简单标签
SimpleTag接口详解
voidsetJspContext(JspContextpc):设置当前JSP的上下文件。容器调用
voidsetParent(JspTagparent):设置父标签对象。如果有的话。容器调用
voidsetJspBody(JspFragmentjspBody):设置主体内容。容器调用
若实现此接口类,有相关的setter属性,容器会自动调用自定义的setter方法。
voiddoTag():遇到标签就执行。容器调用。---------处理自定义标签功能的方法
用于完成所有的标签逻辑,包括输出、迭代、修改标签体内容等。在doTag方法中可以 抛出javax.servlet.jsp.SkipPageException异常,用于通知WEB容器不再执行JSP页面中 位于结束标记后面的内容,这等效于在传统标签的doEndTag方法中返回 Tag.SKIP_PAGE常量的情况。
JspTaggetParent():自己调用。得到父标签。没有返回null
注意:每次调用都会重新实例化,简单标签不会驻留内存,而传统标签会驻留内存(像Servlet一样,第一次访问时容器创建实例化后就驻留了)。

//url地址过滤,若不是本站过来的url将被传到toPage值的文件中去。
public void doTag() throws JspException, IOException {
PageContext pc = (PageContext)getJspContext();
HttpServletRequest request = (HttpServletRequest)pc.getRequest();
String referer = request.getHeader("Referer");//Referer
HttpServletResponse response = (HttpServletResponse)pc.getResponse();
if(null == referer ||!referer.startsWith(siteUrl)){ //若不是指定的url开头地址则转过去,否则继续浏览呢,siteUrl为标签另一属性本站的基地址。
if(toPage.startsWith("/")){
response.sendRedirect(toPage);
}else{
String path = request.getRequestURL().toString();
String prePath = path.substring(0, path.lastIndexOf("/"));
response.sendRedirect(prePath+"/"+toPage);
}
}
}
显示类如下,模拟foreach功能:
public class ForEachSimpleTag extends SimpleTagSupport {
private Object items;
private String var;
private Collection collection ;
public void setItems(Object items) {
if(items instanceof List){
collection = (List)items;
}
if(items instanceof Set){
collection = (Set)items;
}
if(items instanceof Map){
collection = ((Map)items).entrySet();
}
if(items.getClass().isArray()){
int len = Array.getLength(items);//获取数组的长度
for(int i=0;i<len;i++)
collection.add(Array.get(items, i));
}
}
public void setVar(String var) {//setter方法容器自动将属性设置进去。
this.var = var;
}
public void doTag() throws JspException, IOException {
PageContext pc = (PageContext)getJspContext();
if(null==collection) return;
for(Object o:collection){
pc.setAttribute(var, o);
getJspBody().invoke(null);
}
}
}
八、tld文件的主要元素
taglib:根元素
tlib-version:版本号
short-name:短名称
uri:名称空间
function:可定义函数
name:标签的名称如contains
function-class:标签的处理类如com.fk.util.ListContainsLabel
function-signature:标签的静态处理方法
如:java.lang.Booleancontains(java.util.List,java.lang.Object)
tag:定义一个标签
name:标签的名称
tag-class:标签的处理类。全名称
body-content:标签的主体内容类型。
可选值:
JSP:主体内容可以是任意内容。服务传统标签
scriptless:主体内容一般是非java脚本内容。服务简单标签
empty:没有主体内容
tagdepentend:指示主体内容当做普通字符串对待
attribute:定义标签的属性
name:属性名称
required:是否是必须属性
rtexprvalue:是否支持表达式(EL表达式或Java表达式)
(runtimeexpressionvalue)