假设 向浏览器输出当前客户的IP地址 ,我们现在可以这样写
<% //获取当前用户的IP地址 String ip=request.getRemoteHost(); out.print("当前用户的IP地址是:"+ip); %>
但我们现在为了要在jsp页面尽量减少java代码,这个时候我们可以向前面学标签一样,自己来定义一个标签,通过调用标签来达到实现显示客户端的ip地址。
首先第一步我们要创建一个普通的java类,继承SimpleTagSupport类,叫标签处理器类(用来处理我们的需求,比如这里,我们用它来做获取ip,同时打印的处理),代码如下
package com.gqx.a_tag; import java.io.IOException; import javax.servlet.http.HttpServletRequest; import javax.servlet.jsp.JspContext; import javax.servlet.jsp.JspException; import javax.servlet.jsp.JspWriter; import javax.servlet.jsp.PageContext; import javax.servlet.jsp.tagext.SimpleTagSupport; /** * 标签的处理器类 * @author Administrator *(1)、继承SimpleTagSupport *(2)、覆盖doTag方法,doTag里没有request的方法,我们没有办法拿到ip地址; * Class SimpleTagSupport Implemented Interfaces: JspTag, SimpleTag,肯定要实现里面的方法 *SimpleTag的方法里面有setJspContext(JspContext pc) 这个方法,传入了JspContext *但是Class JspContext extended by javax.servlet.jsp.JspContext Direct Known Subclasses:PageContext *即JspContext的子类是pageContext,可以通过多态去强转,一旦得到了pageContext,我们就等同于得到了jsp的其余八个内置对象 */ public class ShowIpTag extends SimpleTagSupport{ private JspContext jspContext; //实现setJspContext方法去获取pageContext对象 public void setJspContext(JspContext pc) { this.jspContext=pc; } @Override public void doTag() throws JspException, IOException { //因为jspContext是pageContext的子类,所以可以将jspContext向上转型 PageContext pageContext=(PageContext) jspContext; //通过pageContext去获取request(同样的道理,HttpServletRequest的子类是ServletRequest //Interface ServletReques All Known Subinterfaces: HttpServletRequest HttpServletRequest request= (HttpServletRequest) pageContext.getRequest(); String ip=request.getRemoteHost(); JspWriter writer=pageContext.getOut(); writer.write("使用自定义标签获取ip地址:"+ip); } }
我们可以仿照系统jstl中已经有了的tag标签,参照其原有的代码,在web项目的web-INF目录下创建一个tld文件(我这里叫做gqxtag.tld文件)然后将其没用的地方都给删除掉,剩下的部分代码如下
<?xml version="1.0" encoding="UTF-8" ?> <taglib xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd" version="2.1"> <!-- 标签库的版本 --> <tlib-version>1.2</tlib-version> <!-- 标签库的前缀 --> <short-name>c</short-name> <!-- tld文件的唯一标记 --> <uri>http://www.gqxing.com</uri> <!-- 这是标签的声明 --> <tag> <!-- 标签的名称 --> <name>showIp</name> <!-- 标签处理器类的全名 --> <tag-class>com.gqx.a_tag.ShowIpTag</tag-class> <!-- 输出内容的格式 --> <body-content>scriptless</body-content> </tag> </taglib>
在复制类标签的路径的时候,我们可以到相关类的在这里单击右键,选择Copy Qualified Name,即可得到相关类处理器的路径
接下来我们就可以在jsp页面中去使用它了,首先,和前面的jstl标签使用一样,我们通过taglib指令去声明
<%@taglib uri="http://www.gqxing.com" prefix="gqx" %>
然后就是直接在jsp的body中直接去调用标签了
<gqx:showIp></gqx:showIp>
结果如图:
我们又去仔细看看SimpleTagSupport的源代码发现了和我们一样的代码,
private JspContext jspContext; public void setJspContext(JspContext pc) { this.jspContext=pc; }
public JspContext getJspContext(JspContext pc) { return this.jspContext; }
这样我们就可以简化我们的代码了
public class ShowIpTag extends SimpleTagSupport{ @Override public void doTag() throws JspException, IOException { HttpServletRequest request= (HttpServletRequest) pageContext.getRequest(); String ip=request.getRemoteHost(); JspWriter writer=pageContext.getOut(); writer.write("使用自定义标签获取ip地址:"+ip); } }
自定义标签时如何执行的
首先当服务器启动的时候,开始加载每一个web应用,同时加载每一个web-info下面的所有文件,如web.xml,还有我刚才定义的一个usertag.tld文件。
(1)、服务器访问http://localhost:8080/JspDemo/usertag/demo1.jsp资源文件,将jsp文件翻译成java文件,同时继续编译成class文件,调用生命周期的jsp_Service()方法。
(2)检查jsp文件中通过taglib指令声明的tld文件,是否存在一个名为http://www.gqxing.com的tld文件,没有就报错,有就会去读取文件,,同时调入内存。
(3)、当jsp的字节码读到了<gqx:showIp>这个标签后,就会去上面的tld文件去查找,同时读取 <tag-class>com.gqx.a_tag.ShowIpTag</tag-class>,然后再去构造ShowIpTag的对象,再去调用里面的方法。
自定义标签处理器类的生命周期
刚才再写自定义标签的类的时候,继承了SimpleTag接口,现在可以看看该接口有什么方法。
以下介绍的顺序,就是SimpleTag的生命周期
void setJspContext(JspContext pc) |
设置pageContext对象,传入pageContext(一定调用)通过getJspCotext()方法得到pageContext对象 |
void setParent(JspTag parent) | 设置父标签对象,传入父标签对象,如果没有父标签,则不调用此方法。通过getParent()方法得到父标签对象。 |
void setXXX(值) | 设置属性值 |
void setJspBody(JspFragment jspBody) |
设置标签体内容。标签体内容封装到JspFragment对像中,然后传入JspFragment对象。 通过getJspBody()方法得到标签体内容。如果没有标签体内容,则不会调用此方法 |
void doTag() | 执行标签时调用的方法。(一定调用) |
为了更好的弄清楚,现在打开翻译了的demo1_jsp.java源文件,发现在jsp_service()的方法中除了jsp页面向外输出的方法外,发现了其中包含了一个方法
.......//输出语句 out.write(" \t "); if (_jspx_meth_gqx_005fshowIp_005f0(_jspx_page_context)) return; out.write("\r\n"); .......//输出语句
接着在该文件的最下方又会发现该方法的详细内容,如下
//传入pageContext,pageContext可以获取其余八个内置对象 private boolean _jspx_meth_gqx_005fshowIp_005f0(PageContext _jspx_page_context) throws Throwable { PageContext pageContext = _jspx_page_context; JspWriter out = _jspx_page_context.getOut(); // gqx:showIp 实例化该对象(前面定义的类) com.gqx.a_tag.ShowIpTag _jspx_th_gqx_005fshowIp_005f0 = new com.gqx.a_tag.ShowIpTag(); org.apache.jasper.runtime.AnnotationHelper.postConstruct(_jsp_annotationprocessor, _jspx_th_gqx_005fshowIp_005f0); //前面看到的在SimpleTag的方法里面就有一个setJspContext方法去封装,之后在其他方法中可以通过getJspContext方法去获取对象 _jspx_th_gqx_005fshowIp_005f0.setJspContext(_jspx_page_context); //就是我们覆写的doTag方法 _jspx_th_gqx_005fshowIp_005f0.doTag(); org.apache.jasper.runtime.AnnotationHelper.preDestroy(_jsp_annotationprocessor, _jspx_th_gqx_005fshowIp_005f0); return false; } }
上面代码我们没有用到setJspBody方法,这个时候,可以在原来的jsp页面去添加标签内容
<gqx:showIp>gqx</gqx:showIp>
我们会发现上面的代码就发生了变化,在其中加入了setJspBody方法
//标签内容通过help封装后,由setJspBody传入,同理,可以在后面可以通过get方法去获取,然后在类中去做相关的处理 _jspx_th_gqx_005fshowIp_005f0.setJspBody(new demo1_jspHelper( 0, _jspx_page_context, _jspx_th_gqx_005fshowIp_005f0, null));