为什么要使用自定义标签?
JSTL标签库只提供了简单的输出等功能,没有实现任何的HTML代码封装,并且某些复杂类型转换,或者逻辑处理的时候,JSTL标签库完成不了,需要自定义标签!
编写自定义标签的步骤:
- 编写一个实现Tag接口的Java类【标签处理器类】
- 在WEB-INF目录下创建tld(Tag Library Descriptor)文件,在tld文件中对标签处理类(实现Tag接口的Java类)进行描述
快速入门
- 目标:使用标签输出客户机的IP地址!
- 按照步骤来:首先编写一个实现Tag接口的Java类
public class showIp implements Tag { @Override public void setPageContext(PageContext pageContext) { } @Override public void setParent(Tag tag) { } @Override public Tag getParent() { return null; } @Override public int doStartTag() throws JspException { return 0; } @Override public int doEndTag() throws JspException { return 0; } @Override public void release() { } }
- 既然要获取到客户机的IP地址,那么request对象是必不可少的。现在问题来了,在Tag重写的方法好像不能直接获取到request对象啊。
- 经过我一番仔细的观察,发现了下面这个方法:
@Override public void setPageContext(PageContext pageContext) { }
既然能获取到pageContext对象,那么其他8大内置对象还不是随随便便?于是乎,我就定义一个成员变量pageContext,在setPageContext()方法中传递过来的pageContext赋值给我定义的成员变量即可!
private PageContext pageContext = null; @Override public void setPageContext(PageContext pageContext) { this.pageContext = pageContext; }
好的,看回我们的需求:使用标签输出客户机的IP地址。在上面剩余5个方法中,最有可能就是在doStartTag()方法中编写代码!
@Override public int doStartTag() throws JspException { //获取到request对象 HttpServletRequest httpServletRequest = (HttpServletRequest) pageContext.getRequest(); //获取到客户机的ip地址 String ip = httpServletRequest.getRemoteAddr(); //获取输出到浏览器的对象 JspWriter jspWriter = pageContext.getOut(); //下面的异常只能捕获,因为子类的异常不能比父类多 try { jspWriter.write(ip); } catch (IOException e) { e.printStackTrace(); } return 0; }
接着,编写tld文件,描述实现Tag接口的Java类【标签处理类】。
<?xml version="1.0" encoding="ISO-8859-1"?> <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.0</tlib-version> <short-name>zhongfucheng</short-name> <uri>/zhongfucheng</uri> <!-- Invoke 'Generate' action to add tags or functions --> <tag> <name>viewIp</name> <tag-class>tag.showIp</tag-class> <body-content>empty</body-content> </tag> </taglib>
下面我们来测试一下看能不能用
标签处理类详细说明
看完上面的程序,大部分人都是懵逼的。因为还不知道它具体是怎么用的,调用顺序是什么。
- 首先我们来看一下Tag接口的源码!
public interface Tag extends JspTag { int SKIP_BODY = 0; int EVAL_BODY_INCLUDE = 1; int SKIP_PAGE = 5; int EVAL_PAGE = 6; void setPageContext(PageContext var1); void setParent(Tag var1); Tag getParent(); int doStartTag() throws JspException; int doEndTag() throws JspException; void release(); }
- 上面程序的执行流程:
- JSP引擎遇到自定义标签,首先创建标签处理器类的实例对象。
- JSP引擎实例化完标签处理器类后,调用setPageContext()方法,将pageContext对象传递给标签处理器类,使得标签处理器类可以通过pageContext对象与JSP页面进行通信!
- setPageContext()方法执行完后,调用setParent()方法,将当前标签的父标签传递给当前处理器类,如果当前标签没有父标签,则传入null
- 当WEB容器执行到自定义标签的开始标记时,调用doStartTag()方法。
- 当WEB容器执行到自定义标签的结束标记时,调用doEndTag()方法。
- 一般来说,当WEB容器执行完自定义标签后,标签处理器类会驻留在内存中,直至停止WEB应用时,WEB容器才会调用release()方法
- 我们现在已经清楚了方法的执行顺序了,可Tag接口的源码还有4个变量阿,它们是用来做什么的呢?我们在编写JSP页面时,经常需要在页面中引入一些逻辑,例如:
- 控制JSP页面某一部分(标签体)是否执行
- 控制整个JSP页面是否执行
- 控制JSP页面内容重复执行
- 修改JSP页面内容输出
- 再看回4个变量的名字,我们可以发现,这4个变量就是用来做逻辑判断的!
- 我们来测试一下吧,在doEndTag()方法中,返回的是SKIP_PAGE变量,看下会怎么样!
@Override public int doEndTag() throws JspException { return SKIP_PAGE; }