自定义JSP标签库开发

jopen 11年前

1、自定义标签简介

1自定义标签主要用于移除Jsp页面中的java代码

2使用自定义标签移除jsp页面中的java代码,只需要完成以下两个步骤:

编写一个实现Tag接口的Java(标签处理器类)

编写标签库描述符(tld)文件,在tld文件中对标签处理器类描述成一个标签

参考tomcat中的examples 项目中jsp 部分

(3)快速入门:使用标签输出客户机IP

<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>

<%@ taglib uri="http://www.itheima.com/MyTag" prefix="myTag" %>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">

<html>

  <head>

  </head>

  <body>

  <hr><h1>java代码输出客户机ip</h1>

   <%

   String ip = request.getRemoteAddr();

out.write(ip);

    %>

    <hr><h1>自定义标签输出客户机ip</h1>

    <myTag:ip/>

  </body>

</html>

<?xml version="1.0" encoding="UTF-8"?>

<taglib version="2.0" 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 http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd">

 <tlib-version>1.0</tlib-version>

 <short-name>myTag</short-name>

 <uri>http://www.itheima.com/MyTag</uri>

 <tag>

  <name>ip</name>

  <tag-class>com.itheima.tag.IpTag</tag-class>

  <body-content>empty</body-content>

 </tag>

package com.itheima.tag;

import java.io.IOException;

import javax.servlet.jsp.JspException;

import javax.servlet.jsp.PageContext;

import javax.servlet.jsp.tagext.Tag;

public class IpTag implements Tag {

public IpTag(){

System.out.println("IpTag处理类对象被创建");

}

private PageContext pc = null;

public int doEndTag() throws JspException {

try {

System.out.println("doEndTag方法被调用");

String ip = pc.getRequest().getRemoteAddr();

pc.getOut().write(ip);

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

return 0;

}

public int doStartTag() throws JspException {

System.out.println("dostarttag  方法被调用");

return 0;

}

public Tag getParent() {

// TODO Auto-generated method stub

return null;

}

public void release() {

System.out.println("release方法执行,释放资源");

}

public void setPageContext(PageContext pc) {

System.out.println("setPageContext方法被调用");

this.pc = pc;

}

public void setParent(Tag t) {

System.out.println("setParent方法被调用");

// TODO Auto-generated method stub

}

}

4分析自定义标签的执行流程

2、Tag接口的执行流程

JSP引擎将遇到自定义标签时,首先创建标签处理器类的实例对象,然后按照JSP规范定义的通信规则依次调用它的方法。

1public void setPageContext(PageContext pc), JSP引擎实例化标签处理器后,将调用setPageContext方法将JSP页面的pageContext对象传递给标签处理器,标签处理器以后可以通过这个pageContext对象与JSP页面进行通信。

2public void setParent(Tag t)setPageContext方法执行完后,WEB容器接着调用的setParent方法将当前标签的父标签传递给当前标签处理器,如果当前标签没有父标签,则传递给setParent方法的参数值为null

3public int doStartTag(),调用了setPageContext方法和setParent方法之后,WEB容器执行到自定义标签的开始标记时,就会调用标签处理器的doStartTag方法。

4public int doEndTag()WEB容器执行完自定义标签的标签体后,就会接着去执行自定义标签的结束标记,此时,WEB容器会去调用标签处理器的doEndTag方法。

5public void release(),通常WEB容器执行完自定义标签后,标签处理器会驻留在内存中,为其它请求服务器,直至停止web应用时,web容器才会调用release方法。

传统标签

Tag接口-----定义了一个标签处理类应具有的最基本的方法和属性(EVAL_BODY_INCLUDE dostart方法返回表示执行标签体,SKIP_BODY dostart方法用,跳过标签体。EVAL_PAGE 

用在doendtag里通知后续页面继续执行,SKIP_PAGE doendtag里通知后续页面不再执行)

|

|----IterationTag接口 (提供了doAfterBody() 在标签体执行过后立即执行,并提供EVAL_BODY_AGAIN doafterbody方法返回表示要重新执行标签体)

|

|----TagSupport(提供了对pageContext的引用)

|

|--BodyTag接口(EVAL_BODY_BUFFERED doStartTag方法中返回通知标签处理器去缓存标签体bodyContent

|

|----BodyTagSupprotgetBodyContent() 获取缓存对象bodyContent

3、自定义标签功能扩展

1开发人员在编写Jsp页面时,经常还需要在页面中引入一些逻辑,例如:

控制jsp页面某一部分内容是否执行。<c:if>

控制整个jsp页面是否执行。

控制jsp页面内容重复执行。<c:forEach>

修改jsp页面内容输出。<c:out> HTML转义

2自定义标签除了可以移除jsp页面java代码外,它也可以实现以上功能。

3tld文件中的四种标签体类型

EMPTY  JSP  scriptless  tagdepentend

例一:控制标签体内容是否执行

doStartTag

EVAL_BODY_INCLUDE  执行标签内容

SKIP_BODY  跳过标签内容

Tld配置

 <tag>

  <name>demo1</name>

  <tag-class>mytag.MyTag1</tag-class>

  <body-content>JSP</body-content>

 </tag>

例二:控制标签后的jsp页面是否执行

doEndTag

EVAL_PAGE  执行标签后的页面内容

SKIP_PAGE  跳过标签后的页面内容

Tld配置

 <tag>

  <name>demo2</name>

  <tag-class>mytag.MyTag2</tag-class>

  <body-content>empty</body-content>

 </tag>

例三:控制jsp页面内容重复执行

doStartTag

EVAL_BODY_INCLUDE  执行标签内容

doAfterBody

EVAL_BODY_AGAIN  重复执行标签内容

SKIP_BODY  跳过标签内容

doAfterBody代码

times--;

if (times > 0) {

    return EVAL_BODY_AGAIN;

} else {

    return SKIP_BODY;

}

例四:修改标签体内容输出

1将标签体内容大写输出

2MyTag4 extends BodyTagSupport

3doStartTag

EVAL_BODY_BUFFERED

4doEndTag

String content = getBodyContent().getString();

pageContext.getOut().write();

4、开发带属性的标签

(1)自定义标签可以定义一个或多个属性,这样,在JSP页面中应用自定义标签时就可以设置这些属性的值,通过这些属性为标签处理器传递参数信息,从而提高标签的灵活性和复用性。

2要想让一个自定义标签具有属性,通常需要完成两个任务:

在标签处理器中编写每个属性对应的setter方法

TLD文件中描术标签的属性

3为自定义标签定义属性时,每个属性都必须按照JavaBean的属性命名方式,在标签处理器中定义属性名对应的setter方法,用来接收JSP页面调用自定义标签时传递进来的属性值。 例如属性url,在标签处理器类中就要定义相应的setUrl(String url)方法。

4在标签处理器中定义相应的set方法后,JSP引擎在解析执行开始标签前,也就是调用doStartTag方法前,会调用set属性方法,为标签设置属性。

5、在TLD中描述标签属性 attribute

例五:控制jsp页面内容重复执行

(1)通过成员变量 和 setter方法设置属性  times

(2)Tld配置

<tag>

… ...

<attribute>

 <name>times</name>

 <required>true</required>

 <rtexprvalue>true</rtexprvalue>

 </attribute>

</tag> 

6简单标签

由于传统标签使用三个标签接口来完成不同的功能,显得过于繁琐,不利于标签技术的推广, SUN公司为降低标签技术的学习难度,在JSP 2.0中定义了一个更为简单、便于编写和调用的SimpleTag接口来实现标签的功能。实现SimpleTag接口的标签通常称为简单标签。简单标签共定义了5个方法:

setJspContext方法

setParentgetParent方法

setJspBody方法

doTag方法

7、SimpleTag方法介绍

1setJspContext方法

用于把JSP页面的pageContext对象传递给标签处理器对象 

2setParent方法

用于把父标签处理器对象传递给当前标签处理器对象 

3getParent方法

用于获得当前标签的父标签处理器对象 

4setJspBody方法

用于把代表标签体的JspFragment对象传递给标签处理器对象 

5doTag方法

用于完成所有的标签逻辑,包括输出、迭代、修改标签体内容等。在doTag方法中可以抛出javax.servlet.jsp.SkipPageException异常,用于通知WEB容器不再执行JSP页面中位于结束标记后面的内容,这等效于在传统标签的doEndTag方法中返回Tag.SKIP_PAGE常量的情况。 

6简单标签(简单标签的标签体不能包含脚本内容,所以tld文件中配置body-content时不能配置为JSP

SimpleTag接口

|

|---SimpleTagSupport实现类(getJspContext() 获取代表jsp页面的pageContextgetJspBody() 代表标签体内容的JspFragment对象)

JspFragmentinvoke(Writer out) 此方法一经调用会将标签体输出到对应的流中。如果你直接给一个null默认输出到pageContext.getOut()

如果doTag()方法抛出SkipPageException异常,标签后的jsp页面就不再执行。

7自定义标签声明属性:在标签处理类中提供setXXX方法,并在tld文件中通过<attribute>标签声明属性的特性即可。

8SimpleTag接口方法的执行顺序

1web容器开始执行标签时,会调用如下方法完成标签的初始化

WEB容器调用标签处理器对象的setJspContext方法,将代表JSP页面的pageContext对象传递给标签处理器对象。

WEB容器调用标签处理器对象的setParent方法,将父标签处理器对象传递给这个标签处理器对象。注意,只有在标签存在父标签的情况下,WEB容器才会调用这个方法。

如果调用标签时设置了属性,容器将调用每个属性对应的setter方法把属性值传递给标签处理器对象。如果标签的属性值是EL表达式或脚本表达式,则WEB容器首先计算表达式的值,然后把值传递给标签处理器对象。

如果简单标签有标签体,容器将调用setJspBody方法把代表标签体的JspFragment对象传递进来。

2执行标签时:

容器调用标签处理器的doTag()方法,开发人员在方法体内通过操作JspFragment对象,就可以实现是否执行、迭代、修改标签体的目的。

9、JspFragment类 

1javax.servlet.jsp.tagext.JspFragment类是在JSP2.0中定义的,它的实例对象代表JSP页面中的一段符合JSP语法规范的JSP片段,这段JSP片段中不能包含JSP脚本元素。

2WEB容器在处理简单标签的标签体时,会把标签体内容用一个JspFragment对象表示,并调用标签处理器对象的setJspBody方法把JspFragment对象传递给标签处理器对象。JspFragment类中只定义了两个方法,如下所示:

3getJspContext方法

用于返回代表调用页面的JspContext对象. ---- pageContext

4public abstract void invoke(java.io.Writer out)  -- 输出标签体内容

用于执行JspFragment对象所代表的JSP代码片段

参数out用于指定将JspFragment对象的执行结果写入到哪个输出流对象中,如果传递给参数out的值为null,则将执行结果写入到JspContext.getOut()方法返回的输出流对象中。(简而言之,可以理解为写给浏览器)

10、invoke方法详解  

JspFragment.invoke方法是JspFragment最重要的方法,利用这个方法可以控制是否执行和输出标签体的内容、是否迭代执行标签体的内容或对标签体的执行结果进行修改后再输出。例如:

在标签处理器中如果没有调用JspFragment.invoke方法,其结果就相当于忽略标签体内容;

在标签处理器中重复调用JspFragment.invoke方法,则标签体内容将会被重复执行;

若想在标签处理器中修改标签体内容,只需在调用invoke方法时指定一个可取出结果数据的输出流对象(例如StringWriter),让标签体的执行结果输出到该输出流对象中,然后从该输出流对象中取出数据进行修改后再输出到目标设备,即可达到修改标签体的目的。

tld文件配置自定义标签

<tag>

<name>demo10</name>-----定义标签的名字

<tag-class>cn.itheima.simple.Demo10</tag-class>----标签处理类

<body-content>scriptless</body-content>----标签体内容JSP表示任意jsp内容,empty代表只能为空、scriptless带表不能包含脚本内容、tagdependent代表其中的内容是用来给服务器用的,不是输出到浏览器的内容

<attribute>

<name>times</name>---属性的名字(名字必须有)

<required>true</required>---是否是一个必须的属性

<rtexprvalue>true</rtexprvalue>---是否支持el表达式

<type>int</type>---属性的类型

</attribute>

</tag>

package com.itheima.simple;

import java.io.IOException;

import java.io.StringWriter;

import javax.servlet.jsp.JspException;

import javax.servlet.jsp.SkipPageException;

import javax.servlet.jsp.tagext.JspFragment;

import javax.servlet.jsp.tagext.SimpleTagSupport;

public class Demo2 extends SimpleTagSupport {

@Override

public void doTag() throws JspException, IOException {

//控制标签体是否执行--什么都不做,默认情况下标签体不执行 --执行标签体

//JspFragment fragment =  getJspBody();

//fragment.invoke(null);

//控制标签之后的剩余页面是否执行 -- 什么都不做,默认情况下标签之后的剩余页面将会执行 -- 标签之后的内容不执行

//throw new SkipPageException();

//重复执行标签体

//JspFragment fragment =  getJspBody();

//for(int i=0;i<5;i++)

//fragment.invoke(null);

//修改标签体进行输出

StringWriter writer = new StringWriter();

getJspBody().invoke(writer);

String str = writer.toString();

str = str.toUpperCase();

getJspContext().getOut().write(str);

}

}

例一:控制标签体内容是否执行

doTag

JspFragment jspFragment = getJspBody();

jspFragment.invoke(getJspContext().getOut());

jspFragment.invoke(null);

Tld配置

 <tag>

  <name>demo1</name>

  <tag-class>simple.MyTag1</tag-class>

  <body-content>scriptless</body-content>

 </tag>

package com.itheima.simple;

import java.io.IOException;

import javax.servlet.jsp.JspException;

import javax.servlet.jsp.tagext.JspFragment;

import javax.servlet.jsp.tagext.SimpleTagSupport;

public class Test1 extends SimpleTagSupport 

{

   @Override

   public void doTag() throws JspException, IOException {

// TODO Auto-generated method stub

JspFragment jspBody = getJspBody();

jspBody.invoke(null);

}

}

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>

<%@taglib uri="http://www.inheima.com/DyTag" prefix="dt" %>

<%

String path = request.getContextPath();

String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";

%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">

<html>

  <head>

    <base href="<%=basePath%>">

    

    <title>My JSP 'Test2.jsp' starting page</title>

    

  </head>

  

  <body>

  <dt:t1>1111111</dt:t1>

  </body>

</html>

<?xml version="1.0" encoding="UTF-8"?>

<taglib version="2.0" 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 http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd">

 <tlib-version>1.0</tlib-version>

 <short-name>dt</short-name>

 <uri>http://www.inheima.com/DyTag</uri>

 <tag>

 <name>t1</name>

 <tag-class>com.itheima.simple.Test1</tag-class>

 <body-content>scriptless</body-content>

 </tag>

</taglib>

例二:控制标签后的jsp页面是否执行

doTag

throw new SkipPageException()

Tld配置

 <tag>

  <name>demo2</name>

  <tag-class>simple.MyTag2</tag-class>

  <body-content>empty</body-content>

 </tag>

例三:控制jsp页面内容重复执行

doTag

JspFragment fragment = getJspBody();

for (int i = 0; i < times; i++) {

    fragment.invoke(null);

}

Tld配置

 <tag>

  <name>demo3</name>

  <tag-class>simple.MyTag3</tag-class>

  <body-content>scriptless</body-content>

 </tag>

例四:修改jsp页面内容输出

将标签体内容大写输出

doTag

JspFragment fragment = getJspBody();

StringWriter writer = new StringWriter();

fragment.invoke(writer);

getJspContext().getOut().println(

writer.getBuffer().toString().toUpperCase());

<?xml version="1.0" encoding="UTF-8"?>

<taglib version="2.0" 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 http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd">

 <tlib-version>1.0</tlib-version>

 <short-name>dt</short-name>

 <uri>http://www.inheima.com/DyTag</uri>

 <tag>

 <name>t1</name>

 <tag-class>com.itheima.simple.Test1</tag-class>

 <body-content>scriptless</body-content>

 </tag>

 <tag>

 <name>t2</name>

 <tag-class>com.itheima.simple.Test2</tag-class>

 <body-content>scriptless</body-content>

 </tag>

 <tag>

 <name>t3</name>

 <tag-class>com.itheima.simple.Test3</tag-class>

 <body-content>scriptless</body-content>

 </tag>

</taglib>

package com.itheima.simple;

import java.io.IOException;

import java.io.StringWriter;

import javax.servlet.jsp.JspContext;

import javax.servlet.jsp.JspException;

import javax.servlet.jsp.tagext.JspFragment;

import javax.servlet.jsp.tagext.SimpleTagSupport;

public class Test3 extends SimpleTagSupport 

{

  @Override

public void doTag() throws JspException, IOException 

{

   JspFragment jb = getJspBody();

   StringWriter writer=new StringWriter();

   jb.invoke(writer);

   String str = writer.getBuffer().toString().toUpperCase();

   getJspContext().getOut().print(str);

}

}

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>

<%@ taglib uri="http://www.inheima.com/DyTag" prefix="dt" %>

<%

String path = request.getContextPath();

String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";

%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">

<html>

  <head>

    <base href="<%=basePath%>">

    

    <title>My JSP 'Test4.jsp' starting page</title>

    

<meta http-equiv="pragma" content="no-cache">

<meta http-equiv="cache-control" content="no-cache">

<meta http-equiv="expires" content="0">    

<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">

<meta http-equiv="description" content="This is my page">

<!--

<link rel="stylesheet" type="text/csshref="styles.css">

-->

  </head>

  

  <body>

    <dt:t3>ssssssssss</dt:t3> 

  </body>

</html>