今天来看一下自定义标签的内容,自定义标签是JavaWeb的一部分非常重要的核心功能,我们之前就说过,JSP规范说的很清楚,就是Jsp页面中禁止编写一行Java代码,就是最好不要有Java脚本片段,下面就来看一下自定义标签的简介:
自定义标签主要用于移除Jsp页面中的java代码。
移除jsp页面中的java代码,只需要完成两个步骤:
编写一个实现Tag接口的Java类,并覆盖doStartTag方法,把jsp页面中的java代码写到doStartTag方法中。
编写标签库描述符(tld)文件,在tld文件中对自定义标签进行描述。
完成以上操作,即可在JSP页面中导入和使用自定义标签。
快速入门:使用自定义标签输出客户机IP
查看tag接口api文档,分析自定义标签的执行流程。
下面来看一下一个简单的Demo使用自定义标签打印客户机的IP地址
首先我们自定义标签类:ViewIpTag
package com.weijia.traditionaltag;import java.io.IOException;import javax.servlet.http.HttpServletRequest;import javax.servlet.jsp.JspException;import javax.servlet.jsp.JspWriter;import javax.servlet.jsp.tagext.TagSupport;/** * 自定义标签,然后将这个标签映射到这个类:mytag:viewIP * 记得将自定义的标签绑定到一个url上面,这个url一般是公司的网址 * */public class ViewIpTag extends TagSupport{ private static final long serialVersionUID = 1L; @Override public int doStartTag() throws JspException { //内置一个pageContext对象,我们之前说到pageContext对象,它里面是封装了9个隐式对象 HttpServletRequest request = (HttpServletRequest)this.pageContext.getRequest(); JspWriter out = this.pageContext.getOut(); String ip = request.getRemoteAddr(); try { out.print(ip); } catch (IOException e) { throw new RuntimeException(e); } return super.doStartTag(); }}
自定义tld文件,mytag.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"> <description>JSTL 1.1 core library</description> <display-name>JSTL core</display-name> <tlib-version>1.1</tlib-version> <short-name>weijia</short-name> <uri>http://www.weijia.cn/mytag</uri> <!-- 显示IP地址 --> <tag> <description> Catches any Throwable that occurs in its body and optionally exposes it. </description> <name>viewIP</name> <tag-class>com.weijia.traditionaltag.ViewIpTag</tag-class> <body-content>empty</body-content> </tag></taglib>
这里我们将就自定义的标签类就注册好了,下面解释一下这些字段的含义:
首先看一下:
<short-name>这个标签是指定我们定义标签的简称,这个作用不大
<uri>这个标签是给这个标签文件指定一个访问路径,这个路径我们在Jsp页面中引入这个标签的时候需要用到
<tag-class>这个标签就是指定我们自定义的标签类的全称
<body-content>这个标签表明自定义标签是否有标签体内容(empty:没有,JSP:有)
我们注册之后标签类了,下面就在Jsp页面中进行使用了,这时候就要用到我们之前说到的Jsp的指令中的taglib了,格式如下:
<%@ taglib uri="http://www.weijia.cn/mytag" prefix="mytag" %>
这个就将我们定义的标签引入到Jsp页面中了,其中我们uri属性的值就是我们在标签定义文件mytag.tld中指定的那个uri那个标签值,当然这里的uri也可以直接指定mytag.tld文件的路径即:
/WEB-INF/mytag.tld 也是可以的,其实我们查看翻译之后的Jsp代码可以看到,不管用那种方式,他其实加载的时候都是去找真是路径中文件:
其中prefix属性的值是标签前缀名,这个名称就是我们在Jsp页面中使用的标签前缀,这个值一般和tld文件的文件名是保持一致的
下面就是在Jsp中使用标签:
客户机的IP地址是:<mytag:viewIP/>
这样就是打印了客户机的IP地址,这里我们在Jsp页面中就没有Java代码了
上面我们介绍了一个简单的例子,下面我们来详细看一下这个自定义标签的执行原理:
JSP引擎将遇到自定义标签时,首先创建标签处理器类的实例对象,然后按照JSP规范定义的通信规则依次调用它的方法。
1、public void setPageContext(PageContext pc), JSP引擎实例化标签处理器后,将调用setPageContext方法将JSP页面的pageContext对象传递给标签处理器,标签处理器以后可以通过这个pageContext对象与JSP页面进行通信。
2、public void setParent(Tag t),setPageContext方法执行完后,WEB容器接着调用的setParent方法将当前标签的父标签传递给当前标签处理器,如果当前标签没有父标签,则传递给setParent方法的参数值为null。
3、public int doStartTag(),调用了setPageContext方法和setParent方法之后,WEB容器执行到自定义标签的开始标记时,就会调用标签处理器的doStartTag方法。
4、public int doEndTag(),WEB容器执行完自定义标签的标签体后,就会接着去执行自定义标签的结束标记,此时,WEB容器会去调用标签处理器的doEndTag方法。
5、public void release(),通常WEB容器执行完自定义标签后,标签处理器会驻留在内存中,为其它请求服务器,直至停止web应用时,web容器才会调用release方法。
我可以查看我们上面的例子翻译后的Jsp代码:
out.write("<body> \r\n"); out.write("\t<!-- 显示客户机的IP地址 -->\r\n"); out.write("\t客户机的IP地址是:"); if (_jspx_meth_mytag_005fviewIP_005f0(_jspx_page_context)) return; out.write("\r\n"); out.write("\t\r\n");
再来看一下那个if中的方法的代码:
private boolean _jspx_meth_mytag_005fviewIP_005f0(PageContext _jspx_page_context) throws Throwable { PageContext pageContext = _jspx_page_context; JspWriter out = _jspx_page_context.getOut(); // mytag:viewIP com.weijia.traditionaltag.ViewIpTag _jspx_th_mytag_005fviewIP_005f0 = (com.weijia.traditionaltag.ViewIpTag) _005fjspx_005ftagPoo l_005fmytag_005fviewIP_005fnobody.get(com.weijia.traditionaltag.ViewIpTag.class); _jspx_th_mytag_005fviewIP_005f0.setPageContext(_jspx_page_context); _jspx_th_mytag_005fviewIP_005f0.setParent(null); int _jspx_eval_mytag_005fviewIP_005f0 = _jspx_th_mytag_005fviewIP_005f0.doStartTag(); if (_jspx_th_mytag_005fviewIP_005f0.doEndTag() == javax.servlet.jsp.tagext.Tag.SKIP_PAGE) { _005fjspx_005ftagPool_005fmytag_005fviewIP_005fnobody.reuse(_jspx_th_mytag_005fviewIP_005f0); return true; } _005fjspx_005ftagPool_005fmytag_005fviewIP_005fnobody.reuse(_jspx_th_mytag_005fviewIP_005f0); return false; }
我们可以看到,首先这个方法接收的是一个pageContext变量对象,这个和我们之前说的一样,自定义标签类中有一个pageContext变量对象就可以操作其他对象了,下面来看一下那个方法的代码,首先他会去加载那个标签类,同时注意到首先是执行setPageContext()方法的,将pageContext变量传递到标签类中,然后看setParent()方法传递的是null,因为我们打印IP的标签没有父标签的,接下来执行doStartTag()方法,然后再执行doEndTag()方法,这里我们看到是做个判断,如果doEndTag方法返回的值是Tag.SKIP_PAGE的话,就是说余下的jsp页面不执行了,所以返回一个true,那么我们看到上面的if判断代码中,如果这个方法返回true的话,直接return,下面的代码就不执行了。大体流程就是这样的。
下面我们用自定义标签来实现一些特定需求的功能:
1、不执行标签体内容
自定义标签类:
package com.weijia.traditionaltag;import javax.servlet.jsp.JspException;import javax.servlet.jsp.tagext.TagSupport;/** * 是否输出标签体内容 * @author weijiang204321 * */public class TagDemo1 extends TagSupport{ @Override public int doStartTag() throws JspException { return TagSupport.EVAL_BODY_INCLUDE;//输出标签体内容 //return TagSupport.SKIP_BODY;//不输出标签体内容 }}
我们看到只要doStartTag方法返回TagSupport.EVAL_BODY_INCLUDE常量,就会执行标签体内容,如果返回的是TagSupport.SKIP_BODY常量,就不会执行标签体内容,代码很简单。
下面我们再来注册这个标签类:
<!-- 是否显示标签体 --> <tag> <description> Catches any Throwable that occurs in its body and optionally exposes it. </description> <name>demo1</name> <tag-class>com.weijia.traditionaltag.TagDemo1</tag-class> <body-content>JSP</body-content> </tag>
因为是有标签体内容的,所以<body-content>标签的值是JSP
在Jsp页面中使用:
<!-- 不执行标签体 --><simpletag:demo1> aaaa</simpletag:demo1>
自定义标签类:
package com.weijia.traditionaltag;import javax.servlet.jsp.JspException;import javax.servlet.jsp.tagext.TagSupport;/** * 控制整个JSP是否输出 * @author weijiang204321 * */public class TagDemo2 extends TagSupport{ @Override public int doStartTag() throws JspException { return super.doStartTag(); } @Override public int doEndTag() throws JspException { return TagSupport.EVAL_PAGE; //return TagSupport.SKIP_PAGE;不执行余下的jsp内容 }}
当doEndTag方法返回的是TagSupport.EVAL_PAGE常量的话就执行jsp余下的内容,如果返回的是TagSupport.SKIP_PAGE常量的话就不执行jsp余下的内容
在tld文件中注册这个自定义标签类:
<!-- 控制是否显示jsp页面 --> <tag> <description> Catches any Throwable that occurs in its body and optionally exposes it. </description> <name>demo2</name> <tag-class>com.weijia.traditionaltag.TagDemo2</tag-class> <body-content>empty</body-content> </tag>
在JSP页面中使用:
<!-- 不执行余下的页面内容 --><simpletag:demo2/>
这样使用之后,在这个标签之后的内容就不会执行了,我们在上面分析源代码的时候已经解析过了。页面都不会含有余下的内容了,如果我们将这个标签放在页面的第一行,那么这个页面就是一片空白,我们在浏览器中查看页面的源代码,也是发现一片空白的,因为out对象没有进行print了
3、重复执行标签体内容
自定义标签体类:
package com.weijia.traditionaltag;import javax.servlet.jsp.JspException;import javax.servlet.jsp.tagext.TagSupport;/** * 控制标签体重复执行 * @author weijiang204321 * */public class TagDemo3 extends TagSupport{ private int count = 5; @Override public int doStartTag() throws JspException { return TagSupport.EVAL_BODY_INCLUDE; } @Override public int doAfterBody() throws JspException { count--; if(count > 0){ return TagSupport.EVAL_BODY_AGAIN;//执行完之后接着执行doAfterBody()方法 }