首页 > 代码库 > JSP自定义标签

JSP自定义标签

假设 向浏览器输出当前客户的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));

  

 

JSP自定义标签