自定义标签简介

来源:互联网 发布:索泰显卡 知乎 编辑:程序博客网 时间:2024/05/16 09:13

自定义标签简介

关键字:   Tag标签    

自定义标签:
JavaBean和自定义标签的区别:它们都是一个可重用的组件,自定义标签可以与jsp页面通信,javabean不行,它只是一种数据模型,标签可以显示数据,javabean是被动的,等jsp来调用,自定义标签可以操纵jsp,是jsp的一部分. 标签体可以包含文本,jsp脚本,jsp表达式,EL表达式,后三种是把执行结果当作文本来处理.

通常我们写的标签处理文件,Servlet,JavaBean等只要是java类都要存在classes目录下,或者把它们打包成一个jar包存在lib目录下,properties文件放在classes目录下,即类装载器目录下.也可以打包成jar文件放到lib目录下.tld文件不能放在 classes或者lib目录下,只能放在WEB-INF目录或其子目录下.

标签处理文件的功能:
★是否让web容器执行标签体,
★是否重复执行标签体,
★是否输出标签体之前修改标签体的执行结果.
★是否执行结束标签后的jsp页面.

自定义标签几个重要的接口:
1).Tag接口常用的方法和常量:
EVAL_BODY_INCLUDED,SKIP_BODY,EVAL_PAGE,SKIP_PAGE,setPageContext(),
setParent(),getParent(),doStartTag(),doEndTag(),release(),这些方法的调用顺序如下:
setPageContext()—>setParent()—>setters()—>doStartTag()—>doEndTag()—>release().
2).IterationTag接口,继承自Tag接口,常用的方法和常量:
EVAL_BODY_AGAIN,doAfterBody()
3).BodyTag接口,继承自IterationTag接口,常用的方法和常量:
EVAL_BODY_BUFFERED,setBodyContent(),doInitBody()(初始化Body对象).

在jsp的API中BodyTagSupport类已经实现上面的接口,所以我们在编写自定义标签时只需继承这个类,根据需求重写它的一些方法就可以了.
doStartTag():
是否执行自定义标签的标签体,是否创建一个缓冲区对象捕获标签体的执行结果,从而让标签开发者在标签处理种对标签体的执行结果进行修改,其返回值可以是一下三个:
EVAL_BODY_INCLUDE(执行标签体),
SKIP_BODY(跳过标签体),
EVAL_BODY_BUFFERED(把标签体放到缓冲区中,以便进一步处理).
doAfterBody():
其返回值可以为:
EVAL_BODY_AGAIN(表示再执行标签体一次),
SKIP_BODY(表示结束标签体执行继续执行doEndTag)
doEndTag():
控制是否执行结束标签后面的内容, 其返回值可以为:
EVAL_PAGE(表示继续执行结束标签后面的jsp代码);
SKIP_PAGE(表示忽略结束标签后面的jsp代码).
setPageContext():把PageContext传给标签处理器.
setParent():如果自定义标签有父标签,则把父标签传给标签处理器,没有则把null传给标签处理器.

TLD文件简介:
如果 JSP 容器在转换时遇到了自定义标签,那么它就检查标签库描述符(tag library descriptor)(TLD)文件以查询相应的标签处理程序。TLD 文件对于自定义标签处理程序,就像 Web 部署描述符对于 servlet 一样。TLD 文件通常保存在 Web 应用程序的WEB-INF目录,它们一般以.tld扩展名结束。下面就是一个简单的tld文件:
<!--l version="1.0" encoding="UTF-8-->
<!--CTYPE taglib PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN" </sp-->
"http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd">

1.0
2.0
name
MyTagName
标签处理文件的完整的类名
JSP

让我们更详细地分析一下这些标签:

* TLD 文件的根元素是.它描述了一个标签库 —— 即一组标签/标签处理程序对。
* 元素对应于标签库版本。
* 对应于标签库所依赖的 JSP 技术的版本。
* 元素定义了 IDE 和其他开发工具可以使用的标签库的简单名。
* 元素包含许多元素,标签库中每一个标签有一个元素。

这里可以有四个值
empty:表示不能有标签体
JSP:标签体可以是任意一种JSP页面元素
scriptless:不能是JSP页面元素
tagdependent:由标签处理器去处理.jsp引擎不处理标签体,

给自定义标签定义属性:
先在java文件中定义属性和set方法.
然后在tld文件中加入:
description:对属性进行描述
attributename:为属性定义一个名称,不能以jsp,_jsp,java和sun开头.
true:true表示此属性是必需的,false表示可有可无.
true: true表示可以使用jsp表达式赋值.
ObjectType:指定此属性的java类型.

使用动态属性:在tld里面没写有哪些属性.利用集合来实现.
使用属性
让标签处理器实现javax.servlet.jsp.tagext.DynamicAttirbutes接口
public void setDynamicAttribute(String uri,String localName,Object value){}

值得注意的是: pageContext.getOut()返回的Out对象不是不变的.什么都不干的时候返回的是jsp中的out对象.如果返回doStartTag ()方法返回EVAL_BODY_BUFFERED,将调用pushBody()方法后把原来的out对象压到栈顶,同时产生一个新的out对象.
popBody()方法可以将新的对象弹出,调用pageContext.getOut()方法就返回原来的out对象.
BodyContent是JspWriter的子类,bodyContent是一个out对象.bodyContent.getEnclosingWriter()返回的是类型JspWriter的out对象,即jsp页面的out对象.


简单标签库开发:
SimpleTag接口可以完成Tag接口的所有功能.它定义了如下几个方法:
setJspContext(JspContext pc):这个JspContext可以看作是PageContext.
setParent(JspTag parent):有父标签才被调用.
setJspBody(JspFragment jspBody): 如果标签有内容先调用setJspBody()方法.JspFragment就是一个标签体对象,但是不能包含脚本片段和脚本表达式.
doTag():可以处理上述接口中所说的三个方法的所有功能. 如果不想输出标签体,则在doTag()里面什么都不干,如果想循环则for或者while等,如果想改写标签体的内容也在此方法里面改写.要处理标签体内容就调用JspFragment的invoke()方法.如果不想让结束标签后面的jsp代码继续执行就抛出异常.如果JspFragment的 invoke()方法的参数为null,就是把JspBody输出到jsp页面

在简单标签中定义属性:
等同于
aaaaaaaa
使用下面这种方法时,要注意:如果标签有标签体,不能随便写了,应该这样:
这里写你的标签体

标签库的描述和部署:
jsp1.2:tld文件用的是dtd文件
jsp2.0:用的是xsd文件.
1. 可以<!--taglib="" prefix="-->
2. 在web.xml文件中


给标签库文件指定一个URI,任何有意义的名字都可以,
但是通常以”http://”打头
标签库文件的位置


3.也可以把标签文件打成jar包放在web应用程序的lib目录下.

 

JSP中自定义标记符的使用

关键字:   jsp    

摘要
   在JSP中有一种机制,可以让你在JSP页面中插入与HTML类似的标记。本文介绍JSP定制标记的基本概念和构成,以及如何开发和应用JSP定制标记。

关键字
JSP,XML,TLD,标记符

什么是标记
使用HTML语言我们可以这样去编辑我们的网页:

<HTML>

    <HEAD>

<TITLE>

HELLO WORLD

</TITLE>

</HEAD>

<BODY>

HELLO WORLD

</BODY>

</HTML>

在这里我们把</HEAD>,<TITLE>,<BODY>称为标记。HTML 标记( HTML Markup)是HTML文档的控制语言,用于指定浏览器显示和打印文档的方式.它是用小于号"<"和大于号">"括起来的短语和符号,如 <Html>、</Body>等。许多HTMl标记以成对的方式出现,如<TITLE>< /TITLE>、<Body></Body> 等。在JSP中我们也可以定制自己的标记,以供JSP页面使用,如下例所示

<!—login.jsp-->

<%@ taglib uri="/tlds/taglib.tld" prefix="tagclass" %>

<html>

 <head>

<title>login</title>

 </head>

 <body>

   <tagclass:login width="200" height= "100" >

</tagclass:login>

 </body>

</html>

在上例中</tagclass:login>就是一个JSP定制标记符。widtht、height是这个标记的属性。<%@ taglib uri="/tlds/taglib.tld" prefix="tagclass" %>是一个标记库定义指令,在稍后我们将会讨论。在JSP中定制标记符,实质上就是以标记的形式封装了一个俱有独立功能的Java类。标记的使用减少了直接嵌入JSP页面的Java代码,方便了页面的布局,并且有利于代码的复用,提高了开发的效率。

JSP服务器解析标记的过程
那么当一个标记被嵌入JSP页面后,JSP服务器是如何对这个标记进行解析的呢?各对象的含义如下所示:

Client: 表示客户端。

JSP-Server:JSP服务器。

JSP-Page:JSP页面。

TLD:  标记库描述文件,定义标记和标记的各种属性和处理文件等。

TagClass 标记处理程序

当一个用户访问一个JSP页面时,这个请求被发送到JSP服务器,JSP服务器会根据这个请求去调用相应的页面,如果这个页面中有自定义的标记, JSP服务就会根据页面指令<%@ taglib>去访问TLD得到处理程序的相关信息,接着调用该处理程序的构造器方法,启动标记符处理程序,并读取标记符的属性和相应值。对每个没有设置属性的,调用相应的set方法。当标记符第一次使用时,它的任何属性都不会做过设置,因此对每个属性都调用set方法。属性设置完以后,JSP服务器调用处理程序的doStartTag(),然后再调用doEndTag()方法。最后JSP服务器会继续处理剩下的页面,在页面结尾调用release ()方法,清理占用的所有资源。

TLD文件
TLD(TLD:Tag Library Descriptor标记库描述符)文件,标准的XML格式的标记定义文件,被用来存放标记符的信息,下面就是一个典型的TLD文件。

<?xml version="1.0" encoding="ISO-8859-1" ?>

<!—XML的版本及其字符集-->

 

 

<!DOCTYPE taglib

PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.1//EN"

 "http://java.sun.com/j2ee/dtds/web-jsptaglibrary_1_1.dtd">

<!—文档类型定义-->

 

 

<taglib>

<!—此标记说明我们开始描述一个标记库-->

 

 

<tlibversion>1.0</tlibversion>

 <!—标记库的版本-->

 

 

<jspversion>1.1</jspversion>

 <!—所使用的JSP的版本-->

 

 

 <shortname>tagclass</shortname>

 <!—缺省的名称-->

 

 

<tag>

   <name>login</name>

    <!—标记的名称-->

 

 

<tagclass>

tagclass.login.login

<!—处理这个Tag的相应的类的名称-->

 

</tagclass>

   <info>

      <!—对本标记符的描述-->

 

   </info>

<attribute>

<!—开始定义标记的属性-->

 

    <name>height</name>

       <!—属性的名称-->

 

    <required>true</required>

       <!—表示这个属性是不是必须的-->

 

    <rtexprvalue>true</rtexprvalue>

<!—表示这个属性是否可以用JSP的程序段的结果输出-->

   </attribute>

<attribute>

    <name>width</name>

    <required>true</required>

    <rtexprvalue>true</rtexprvalue>

   </attribute>

</tag>

</taglib>

在这个TLD文件中定义了只有一个标记符的标记符库,这个名为login的标记符会调用一个Applet以验证用户的合法性。处理这个标记的类就是 tagclass.login.login。width、height是这个标记的两个属性。属性是在使用标记符时作为参数发送的值。我们可以在上面的示例中增加几个标记,也可以为每个标记添加几个属性。我们开发标记符库时不一定非要从头开始,自己编写一个全新TLD。我们可以使用某个集成的开发的环境,也可以修改上面的例子。

TagLib指令
那么当JSP服务器在解析一个标记符时,它是如何定义一个标记库的呢?这就是TagLib指令的主要责任。

Taglib 指令

定义一个标记库以及其自定义标记的前缀.

JSP 语法

<%@ taglib uri="URIToTagLibrary" prefix="tagPrefix" %>

例子
<%@ taglib uri="/tlds/taglib.tld" prefix="tagclass" %>

<html>

 <head>

<title>login</title>

 </head>

 <body>

   <tagclass:login width="200" height= "100" >

</tagclass:login>

 </body>

</html>

描述

<% @ taglib %>指令声明此JSP文件使用了自定义的标记,同时引用标记库,

也指定了他们的标记的前缀。 你必须在使用自定义标记之前使用<% @ taglib %>指令。

属性

uri="URIToTagLibrary" :Uniform Resource Identifier (URI)根据标记的前缀对自定义的标记进行唯一的命名,URI可以是一个相对或绝对的路径。
  prefix="tagPrefix":在自定义标记之前的前缀。如上例中的</tagclass:login>

标记符的处理程序(Tag handle)
我们还是以一个例子来看下如何实现一个Tag handle。首先是看一下它的类图:

 

让我们再看一下它的代码:

package tagclass.login;

import javax.servlet.jsp.tagext.TagSupport;

import javax.servlet.jsp.*;

import java.io.*;

public class login extends TagSupport

{

public login()

{

super();

}

public int doStartTag() throws JspTagException

{

JspWriter out = pageContext.getOut();

try

{

out.println("<APPLET CODEBASE=applet/login/ CODE=login.class width=200 height=100 > </APPLET>");

}

catch(Exception e)

{

 

}

return SKIP_BODY;

}

publicc int doEndTag()throws JsptagException

{

return EVAL_PAGE;

}

public void release()

{

super.release();

}

public void setWidth(String language)

{

this.width = width;

}

public String getWidth()

{

return this.width;

}

public void setHeight(String height)

{

this.height=height;

}

public String getHeight()

{

return this.height;

}

private String width;

private String height;

}

从以上我们可以看出,实现一个简单的标记符处理程序有几个要求:①增加一个类,使之继承 java.Servlet.jsp.tagext.TagSupport类。这个类提供了java.Servlet.jsp.tagext.Tag接口所要求的所有的方法。另外,还需要使用一些基本的API,使JSP容器能够调用我们自己提供的标记符处理程序。②必须为每个标记符属性分别创建一个 get<attribute>和set<attribute>方法,JSP容器需要使用这些方法处理程序传递参数。③要为标记符处理程序创建一个构造器和自毁器。JSP需要使用构造器启动处理程序。自毁器是在realease()方法中定义的。在处理程序的生命周期结束时,需要调用自毁器释放所占用的资源。④创建两个名为doStartTag()和doEndTag()的方法,执行具体的处理和输出动作。这两个方法是在处理自定义标记符的起始位置和结束位置调用的。它们的返回值是在Tag Interface里定义的静态int,这几个静态值分别是:

SKIP_BODY隐含0 :跳过了开始和结束标签之间的代码。

EVAL_BODY_INCLUDE隐含1:将body的内容输出到存在的输出流中

SKIP_PAGE隐含5 : 忽略剩下的页面。

EVAL_PAGE隐含6:继续执行下面的页

  当然标记符也有它自己的缺点。很不方便的封装过程,有限的功能。对于一些不太复杂和功能单一的逻辑描述,需要传递的参数要求不高时,使用JSP标记,要方便的多。对于大多数的商业逻辑应用,还是使用bean要好的多,也宜于servlet控制。

附录:文章中所用示例的完整代码
JSP代码:login.jsp
<%@ taglib uri="/tlds/taglib.tld" prefix="tagclass" %>

<html>

<head>

<title></title>

</head>

<body>

<tagclass:login width="200" height= "100" >

</tagclass:login>

</body>

</html>

标记符描述库:taglib.tld
<?xml version="1.0" encoding="ISO-8859-1" ?>

<!DOCTYPE taglib

PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.1//EN"

 "http://java.sun.com/j2ee/dtds/web-jsptaglibrary_1_1.dtd">

<taglib>

<tlibversion>1.0</tlibversion>

<jspversion>1.1</jspversion>

 <shortname>tagclass</shortname>

<tag>

   <name>login</name>

<tagclass>

tagclass.login.login

</tagclass>

   <info>

    </info>

<attribute>

    <name>height</name>

    <required>true</required>

    <rtexprvalue>true</rtexprvalue>

   </attribute>

<attribute>

    <name>width</name>

    <required>true</required>

    <rtexprvalue>true</rtexprvalue>

   </attribute>

</tag>

</taglib>

标记符处理程序:login.java
package tagclass.login;

import javax.servlet.jsp.tagext.TagSupport;

import javax.servlet.jsp.*;

import java.io.*;

public class login extends TagSupport

{

public login()

{

super();

}

public int doStartTag() throws JspTagException

{

JspWriter out = pageContext.getOut();

try

{

out.println("<APPLET CODEBASE=applet/login/ CODE=login.class width=200 height=100 > </APPLET>");

}

catch(Exception e)

{

 

}

return SKIP_BODY;

}

publicc int doEndTag()throws JsptagException

{

return EVAL_PAGE;

}

public void release()

{

super.release();

}

public void setWidth(String language)

{

this.width = width;

}

public String getWidth()

{

return this.width;

}

public void setHeight(String height)

{

this.height=height;

}

public String getHeight()

{

return this.height;

}

private String width;

private String height;

}

标记符处理程序中所使用的Applet : login.java
import java.awt.*;

import java.awt.event.*;

import java.applet.*;

public class login extends Applet implements ActionListener

{

private String s_username;

private String s_userpassword;

private Button b_ok;

private Button b_register;

private Label l_username;

private Label l_userpassword;

private TextField t_username;

private TextField t_userpassword;

private GridLayout g_gridlayout;

public void init()

{

b_ok=new Button("ok");

b_register=new Button("register");

l_username= new Label("name");

l_userpassword=new Label("password");

t_username=new TextField();

t_userpassword=new TextField();

b_ok.addActionListener(this);

b_register.addActionListener(this);

g_gridlayout=new GridLayout(3,2,10,10);

this.setLayout(g_gridlayout);

//this.setBackground(Color.blue);

add(l_username);

add(t_username);

add(l_userpassword);

add(t_userpassword);

add(b_ok);

add(b_register);

}

public void actionPerformed(ActionEvent ev)

{

String s_label=ev.getActionCommand();

if (s_label.equals("ok"))

{

t_username.setText("name");

}

if (s_label.equals("register"))

{

t_userpassword.setText("password");

}

}

public void paint(Graphics g)

{

}

}

原创粉丝点击