[转]JSP自定义标签

原文连接html

jsp的内置标签和jstl标签库内的标签都知足不了需求,这时候就须要开发者自定义标签。java

自定义标签

下面咱们先来开发一个自定义标签,而后再说它的原理吧!web

自定义标签的开发步骤 

步骤一

编写一个普通的java类,继承TagSupport~apache

package com.vmaxtam.dotest; import javax.servlet.jsp.tagext.TagSupport; public class MyTagTest extends TagSupport { }

步骤二

重写父类的setPageContext方法,用于获得当前jsp页面的pageContext对象。浏览器

public class MyTagTest extends TagSupport { private PageContext pageContext; @Override public void setPageContext(PageContext pageContext) { this.pageContext=pageContext; } } 

步骤三

重写父类的doStartTag方法,里面写上你定义的标签的java操做,这里我定义的标签用做向浏览器输出一大段信息:服务器

@Override
    public int doStartTag() throws JspException { try { pageContext.getResponse().getWriter().write("这是我写的一大段信息:ABCDEFGHIJKLMNOPQRSTUVWXYZ"); } catch (IOException e) { e.printStackTrace(); throw new RuntimeException(e); } return super.doStartTag(); }

这样就完成一个标签处理程序了~别着急,写完程序咱们还须要注册它。 less

步骤四

在你的web应用目录下,找到WEB-INF文件夹,在里面新建一个tld类型的文件jsp

而后再里面注册你的标签吧:ide

<?xml version="1.0" encoding="ISO-8859-1" ?> <!DOCTYPE taglib PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN" "http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd"> <taglib> <tlib-version>1.0</tlib-version><!-- 表明标签库的版本号 --> <jsp-version>1.2</jsp-version><!-- 表明jsp的版本 --> <short-name>mtt</short-name><!-- 你的标签库的简称 --> <uri>http://vmaxtam.com/mytag</uri><!-- 你标签库的引用uri --> <tag> <name>mytah</name><!-- 你定义的标签的名称 --> <tag-class>com.vmaxtam.dotest.MyTagTest</tag-class><!-- 对应的标签处理程序:包名+类名 --> <body-content>JSP</body-content><!-- 标签体内容的格式 --> </tag> </taglib>

若是你忘记了怎么写,能够参考jstl里的tld文件~post

步骤五

你要在使用你定义的标签的jsp页面导入你的标签库!就像导入类包同样

只需在jsp页面写上下面内容:

<%@taglib uri="http://vmaxtam.com/mytag" prefix="mmt" %>

步骤6

以上5步已经把准备工做都作好了~下面咱们来使用标签吧!

<html> <head> <title>My JSP 'testit.jsp' starting page</title> </head> <body> <mmt:mytag></mmt:mytag> </body> </html>

浏览器效果以下:

这样,咱们就完成了一次自定义标签了,虽然咱们知道步骤,可是不知道为何这样就行,因此,下面来讲一下它的原理:

自定义标签的原理

1)当服务器打开时,就会加载WEB-INF下的资源文件,包括web.xml 和 tld文件,把它们加载到内存

2)咱们在浏览器输入http://localhost:8080/TestArea/testit.jsp来访问jsp页面

3)服务器读取testit.jsp里的内容,当读到

<%@taglib uri="http://vmaxtam.com/mytag" prefix="mmt" %> 

这一句的时候,就会在内存中找是否存在urihttp://vmaxtam.com/mytagtld文件,找不到就会报错

4)继续读取jsp页面,读到<mmt:mytag>这个标签的时候,就会经过uri去找到tld文件,在tld文件中找到mytab是否被定义,是的话就获得它的tag-class的内容,而后去找到它对应的标签处理程序

5)实例化标签处理程序,利用生成的对象调用它里面的方法

这里服务器对标签处理程序里的方法也有必定的调用顺序      

A)void setPageContext(PageContext pc)  --传入pageContext对象

B)void setParent(Tag t)              --若是有父标签,传入父标签对象,若是没有,则传入null

C)int doStartTag()                 --开始执行标签时调用。

D)int doEndTag()                --结束标签时调用

E)void release()                  --释放资源

若是你没有重写上面的方法,系统将会调用它的父类里的方法~

为何会是这个顺序调用,是有证据的,下面咱们来看看jsp被翻译为java源文件里的截取:

private boolean _jspx_meth_itcast_005fshowIp_005f0(PageContext _jspx_page_context) throws Throwable { PageContext pageContext = _jspx_page_context; JspWriter out = _jspx_page_context.getOut(); // itcast:showIp 1) 实例化ShowIpTag对象 gz.itcast.tag.ShowIpTag _jspx_th_itcast_005fshowIp_005f0 = (gz.itcast.tag.ShowIpTag) _005fjspx_005ftagPool_005fitcast_005fshowIp_005fnobody.get(gz.itcast.tag.ShowIpTag.class); 2)调用setPageContext方法_jspx_th_itcast_005fshowIp_005f0.setPageContext(_jspx_page_context); 3)调用setParent方法_jspx_th_itcast_005fshowIp_005f0.setParent(null); 4)调用doStartTag方法 int _jspx_eval_itcast_005fshowIp_005f0 = _jspx_th_itcast_005fshowIp_005f0.doStartTag(); 5)调用doEndTag方法
if (_jspx_th_itcast_005fshowIp_005f0.doEndTag() == javax.servlet.jsp.tagext.Tag.SKIP_PAGE) { _005fjspx_005ftagPool_005fitcast_005fshowIp_005fnobody.reuse(_jspx_th_itcast_005fshowIp_005f0); return true; } _005fjspx_005ftagPool_005fitcast_005fshowIp_005fnobody.reuse(_jspx_th_itcast_005fshowIp_005f0); return false; }

控制标签体内容 与 结束标签后的内容

自定义标签能够可控制标签体内的文本 和 结束标签后的文本是否输出~

    @Override//遇到开始标签时执行的方法 public int doStartTag() throws JspException { //return Tag.SKIP_BODY; //标签体内容不向浏览器输出 return Tag.EVAL_BODY_INCLUDE;//标签体内容向浏览器输出  } @Override//遇到结束标签后执行的方法 public int doEndTag() throws JspException { //return Tag.EVAL_PAGE;//结束标签后的内容输出到浏览器 return Tag.SKIP_PAGE;//结束标签后的内容不输出到浏览器 }

那么如何重复输出标签体内的文本内容呢?TagSupper还提供了一个doAftetBody方法,咱们只须要这样作:

    int i = 4; @Override//每输出一次标签体的内容都会调用一次这个方法 public int doAfterBody() throws JspException { while(true) { if(i>0) { i--; return IterationTag.EVAL_BODY_AGAIN;//再执行一次便签体内的内容 }else{ break; } } return Tag.SKIP_BODY;//不输出标签体的内容 }

以上的内容都是控制标签体的内容输出的问题,那么能不能改变标签体力的内容呢?

很惋惜,用TagSupport是不行,可是咱们能够用它的子类BodyTagSupport,那么久写一个类继承BodyTagSupport类吧~

public class MyTagTest extends BodyTagSupport { private PageContext pageContext; @Override public void setPageContext(PageContext pageContext) { this.pageContext = pageContext; } @Override public int doStartTag() throws JspException { //返回BodyTag.EVAL_BODY_BUFFERED,表示输出标签体内容 //返回Tag.SKIP_BODY,表示不输出内容 return BodyTag.EVAL_BODY_BUFFERED; //return Tag.SKIP_BODY;  } @Override public int doEndTag() throws JspException { //获得BodyContent对象,它包装了标签体里的内容 BodyContent bodyContent = this.getBodyContent(); //利用getString方法获得字符串 String content = bodyContent.getString(); //改变字符串内容,将小写改成大写 String change = content.toUpperCase(); //输出到浏览器 try { this.pageContext.getResponse().getWriter().write(change); } catch (IOException e) { // TODO Auto-generated catch block  e.printStackTrace(); } return Tag.SKIP_PAGE; } }

以上~就是自定义标签的建立步骤会原理,还有一些标签体内容的处理方法,你们以为容易吗?

对,十分的不容易啊,用这种方法定义的标签咱们称为传统标签,因此这是一个社会问题,是时候就会有人站出来,写出一组代码来解决这个问题了,这组代码称为:简单标签

简单标签

为何要学习传统标签

学习传统标签是为了之后维护到一些旧系统!!

简单标签比传统标签简单在标签处理器类的编写简单了!!!

简单便签的开发步骤

一样的,咱们先学习简单标签的开发步骤,而后再说说它的原理

步骤一

编写标签处理器类,也就是一个普通的类,继承SimpleTagSupport类。而后重写它的doTag()方法:

public class MySimpleTag extends SimpleTagSupport { @Override//当遇到标签时就会执行这个方法 public void doTag() throws JspException, IOException { System.out.println("执行了简单标签里的doTag()方法~"); } }

步骤二

tld文件内注册这个标签吧~这个过程和传统标签同样

   <tag> <name>simpletag</name> <tag-class>com.vmaxtam.dotest.MySimpleTag</tag-class> <body-content>scriptless</body-content><!--这里要用这个处理--> </tag>

步骤三

JSP文件中导入标签库(这个过程和传统标签同样)

步骤四

使用该标签(这个过程和传统标签同样)

以上就是简单标签的定义过程了,和传统标签相比,他简单就简单在不用重写不少方法。

简单标签的原理

一)和传统标签同样,获得tag-class字符串,找到标签处理程序类

二)实例化标签处理程序类

三)利用对象调用方法。。。。

和传统标签相比,简单标签调用的方法不相同:

SimpleTag接口的方法执行过程:

 1) void setJspContext(JspContext pc)  --设置pageContext对象,传入pageContext对象。JspContext是PageContext的父类。在标签处理器类中经过this.getJspContext()方法获得PageContext对象。
 2)void setParent(JspTag parent)        --传入父标签对象,若是没有父标签,则不调用次方法。经过getParent方法获得父标签对象
 3)void setJspBody(JspFragment jspBody)   --传入标签体内容。标签体内容封装到JspFragment方法中。经过getJspBody方法获得标签体内容。若是没签体,不调用次方法。
 4)void doTag()                       --开始标签和结束标签都执行次方法。

为何是这样调用方法呢,也是有证据的:

复制代码
private boolean _jspx_meth_itcast_005fsimpleDemo_005f0(PageContext _jspx_page_context) throws Throwable { PageContext pageContext = _jspx_page_context; JspWriter out = _jspx_page_context.getOut(); // itcast:simpleDemo 1)实例化SimpleDemo对象 gz.itcast.b_simple.SimpleDemo _jspx_th_itcast_005fsimpleDemo_005f0 = new gz.itcast.b_simple.SimpleDemo(); org.apache.jasper.runtime.AnnotationHelper.postConstruct(_jsp_annotationprocessor, _jspx_th_itcast_005fsimpleDemo_005f0); 2)调用setJspContext方法,传入PageContext对象 _jspx_th_itcast_005fsimpleDemo_005f0.setJspContext(_jspx_page_context); 3)调用setParent方法,若是没有父标签,不执行。 4)调用setJspBody方法,传入标签体内容 _jspx_th_itcast_005fsimpleDemo_005f0.setJspBody(new Helper( 0, _jspx_page_context, _jspx_th_itcast_005fsimpleDemo_005f0, null)); 5)调用doTag方法,执行标签 _jspx_th_itcast_005fsimpleDemo_005f0.doTag(); org.apache.jasper.runtime.AnnotationHelper.preDestroy(_jsp_annotationprocessor, _jspx_th_itcast_005fsimpleDemo_005f0); return false; }
复制代码

控制标签体文本 与 结束标签后内容 是否输出

咱们能够经过JspFragment对象来控制的~

标签体内容:

要输出: 在doTag()方法中执行jspFrament.invoke()方法

不输出: 什么都不作!!

结束标签后的内容:

要输出:什么都不作!

不输出:在doTag()方法中抛出一个SkipPageException异常~

@Override
        public void doTag() throws JspException, IOException { JspFragment jspBody = this.getJspBody(); jspBody.invoke(null); throw new SkipPageException(); }

那么如何循环输出标签体内容呢,在简单标签中实现十分简单,在doTag方法中写上

    for(int i=1;i<=5;i++){ jspBody.invoke(null);//默认写出都浏览器 }

改变标签体里的内容

doTag方法中写上:

//4.1 建立一个临时的Writer输出流(容器) StringWriter writer = new StringWriter(); //4.2 把标签体内容拷贝到临时的Writer流中 JspFragment jspBody = this.getJspBody(); jspBody.invoke(writer); //4.3 从临时的Writer流中取出标签体内容 String content = writer.toString(); //4.4 改变标签体内容 content = content.toUpperCase(); //4.5 把改变后的内容写出到浏览器中 //jspBody.invoke(null);若是这样写,那么仍是输出原来的内容 this.getJspContext().getOut().write(content);

标签体内容的输出格式

除了能设置标签体内容是否输出,还可以设置它的输出格式,那么它有什么样的输出格式呢?

能够有如下输出格式:

JSP: 表示输出的标签体内容能够包含jsp脚本,且能够执行此脚本。此值只能用在传统标签中。

scriptless: 表示输出的标签体内容不能包含jsp脚本,若是包含则报错。  

empty:表示没有标签体内容。便是空标签。若是不是空标签,则报错。

tagdependent: 表示输出的标签体内容能够包含jsp脚本。但不执行jsp脚本(直接原样输出)

那么咱们要在tld文件内设置文本的输出格式:

  <tag> <name>tagDemo</name> <tag-class>gz.itcast.a_tag.TagDemo1</tag-class> <body-content>JSP</body-content><!--在这里设置--> </tag>

上面都是在讨论标签体内容的输出,标签里还能够设置属性的,那么自定义标签如何定义标签的属性呢?

 

自定义标签的属性

这个过程咱们在简单标签内实现,如下是操做步骤

步骤一

在标签处理器类内声明一个成员变量,,这个成员变量就用来接受标签属性的值,而后再标签处理器类内为这个成员变量生成一个setter方法:

public class MySimpleTag extends SimpleTagSupport { private Integer num; public void setNum(Integer num) { this.num = num; }

步骤二

要到tld文件注册这个属性,属性药注册在响应标签的<Tag>标签内

  <tag> <name>simpletag</name> <tag-class>com.vmaxtam.dotest.MySimpleTag</tag-class> <body-content>scriptless</body-content> <attribute> <name>num</name> <!-- ??? --> <required>true</required><!-- ???????????????? --> <rtexprvalue>true</rtexprvalue><!-- ???????EL??? --> </attribute> </tag>

步骤三

这样就能够去使用属性了~ 

<body> <mmt:simpletag num="1001">我是标签里的内容</mmt:simpletag>我是标签后的内容 </body>

上面的内容就能够建立一个基本功能的自定义标签了~

相关文章
相关标签/搜索