Java精选笔记_自定义标签

时间:2024-06-28 10:07:26
自定义标签

自定义标签入门


什么是自定义标签


自定义标签可以有效地将HTML代码与Java代码分离,从而使不懂Java编程的HTML设计人员也可以编写出功能强大的JSP页面

JSP规范中定义了多个用于开发自定义标签的接口和类,它们都位于javax.servlet.jsp.tagext包中





JSP自定义标签是用户定义的JSP语言元素,可以看成是一种通过标签处理器生成基于XML脚本的方法。





自定义标签在功能上和逻辑上都与JavaBean类似,都是一组可重用的组件代码。相较于JavaBean,自定义标签可以使Web开发者可以完全从Java编程中脱离开来,专注于页面显示和格式上面去,所以具有广阔的发展前景。

自定义标签的构成


一个自定义标签一般由JavaBean、标签库描述、标签处理器、web.xml文件配置、标签库声明等元素所构成。

自定义标签声明


当JSP页面中引用自定义标签时,用来在页面上对自定义标签进行声明的。

taglib编译指令的作用主要是定义一个标签库路径及其前缀

<%@ taglib="URLToTagLibrary" prefix="tagPrefix"%>

标签库描述符文件


是一个描述标签库的XML文档

TLD包含有关整个库以及库中包含的每一个标签的信息。它把自定义标签与对应的处理程序关联起来。TLD文件名称必须扩展名为.tld。





TLD文件存储在Web模块的WEB-INF目录下或者子目录下,并且一个标签库要对应一个标签库描述文件,而在一个描述文件中可以包含多个自定义标签的声明





<taglib>子元素

元素
说明
元素
说明

<tlib-version>
用于设置标签库版本
<small-icon>
用于设置标签库的可选小图标

<jsp-version>
用于设置标签库要求的JSP规范版本
<large-icon>
用于设置标签库的可选大图标

<short-name>
用于设置该标签库的助记名
<description>
用于设置标签库的描述信息

<uri>
唯一标识该标签库的URI
<listener>
用于设置标签库的监听器类

<display-name>
用于设置标签库显示的可选名
<tag>
用于设置标签库的具体标签

真正用来查找标签库中具体标签的是<tag>元素





<tag>子元素

元素
说明
元素
说明

<name>
用于设置标签的唯一名称
<small-icon>
用于设置标签的可选小图标

<tag-class>
用于设置标签处理器的完全限定名
<large-icon>
用于设置标签的可选大图标

<tei-class>
用于设置脚本变量信息的子类名称
<description>
用于设置标签的描述信息

<body-content>
用于设置标签的正文内容类型
<variable>
用于设置标签的脚本变量信息

<display-name>
用于设置标签显示的可选名
<attribute>
用于设置标签的属性信息

标签处理器


把自定义标签的主体和属性转变为HTML代码的实际工作,是由标签处理器来完成的。

标签处理器也叫标签处理类,它是一个Java类。当JSP容器编译自定义标签时,就会需要使用标签处理器类的实例。





标签处理器虽然是一个Java类,但不仅仅是一个普通的Java类,在定义时需要满足特殊的要求。开发的标签处理类必须实现Tag或者BodyTag接口类(它们包为javax.servlet.jsp.tagext)

BodyTag接口是继承了Tag接口的子接口。如果创建的自定义标签不带体式,可以实现Tag接口,但是如果创建的自定义标签带体,则需要实现BodyTag接口。





Tag接口类方法

方法名
方法描述

setPageContext(PageContext pc)
设置当前页面的上下文

setParent(Tag t)
设置这个标签处理类的父类

getParent()
获得父类

doStartTag()
处理这个实例中的开发标签

doEndTag()
处理这个实例中的结束标签

release()
由标签处理类引起,来释放状态





BodyTag子接口类重新定义了两个新方法

方法名
方法描述

setBodyContent(BodyContent b)
为体中代码作初始化

doInitBody()
为标签体中的内容设置属性

在标签处理器中定义了标签处理方法doStartTag()和doEndTag(),这两个方法分别在标签开始和结束时执行处理和输出动作。

这两个方法都要求分别返回一个状态码,通知JSP容器岁自定义标签的处理结果及整个JSP页面的运行状态

状态码一共有四种

生命周期

生成servlet需要创建标签处理器类的一个实例

初始化标签处理器,是servlet获知其存在性

如果标签具有属性,属性的取值通过处理器提供setter方法传入到对象

调用标签处理器的doStrartTag()方法并返回一个整数值

标签体被评估或忽略后调用标签处理器的doEndTag()方法

调用标签处理器的release()方法销毁标签处理器实例

自定义标签的开发步骤


1、编写标签处理器

开发自定义标签的核心任务就是要编写作为标签处理器的Java类。





传统标签开发

实现javax.servlet.jsp.tagext.Tag接口

调用doStartTag()方法





简单标签开发

实现javax.servlet.jsp.tagext.SimpleTag接口

调用doTag()方法





2、编写标签库描述符(tld)文件





要想让JSP引擎在遇到自定义标签时,能找到其所对应的标签处理器类,还必须编写一个标签库描述符(Tag Library Descriptor)文件,简称TLD文件。





一个标签处理器类要想被JSP容器找到并调用,必须在TLD文件中进行注册, 一个TLD文件中可以注册多个标签处理器类,每个自定义标签的注册名称不能相同,同一个TLD文件中注册的多个标签处理器类就形成了一个自定义标签库。





TLD文件是基于XML文件的,其内容的编写需要遵循XML语法规范。





3、在JSP页面导入和使用自定义标签





TLD文件编写完成后,就可以在JSP文件中使用自定义标签。在使用自定义标签之前,首先需要使用taglib指令来引入TLD文件

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

uri属性用于指定引用的是哪一个TLD文件,它应该和要引入的TLD文件中<uri>元素的值保持一致。

prefix属性用于为引入的TLD文件指定一个“引用代号”,在使用这个标签库中注册的自定义标签时都需要加上这个“引用代号”作为前缀。prefix属性的值可以是任意的,但不能和其它taglib指令中的prefix属性值重复,而且需要遵循XML名称空间的命名约定。





自定义标签的格式

空标签

<prefix:tagname />            // 格式1

<prefix:tagname></prefix:tagname>       // 格式2





带标签体的标签

<prefix:tagname>body</prefix:name>





带属性的标签

<prefix:tagname  attrname1 = "attrvalue1" [attrname2 = "attrvalue2" …]>

       [body]

</prefix:tagname>





嵌套标签

<prefix:tagname>

     <prefix:nestedtagname>

           [body]

     </prefix:nestedtagname>

</prefix:tagname>

传统标签


实现Tag接口的标签称为传统标签

Tag接口


是所有传统标签的父接口,它定义了四个int类型的静态常量和六个抽象方法。

定义了JSP页面与标签处理器之间的通信规则,当JSP容器将JSP页面翻译成Servlet源文件时,如果遇到JSP标签,会创建标签处理器类的实例对象,然后依次调用标签处理器的setPageContext()方法、setParent()方法、doStartTag()方法、doEndTag()方法和release()方法,因此,在实现Tag接口时,需要对这些抽象方法进行实现。





静态常量

EVAL_BODY_INCLUDE
doStartTag()方法的返回值,表示标签体会被执行

SKIP_BODY
doStartTag()方法的返回值,表示标签体不被执行

EVAL_PAGE
doEndTag()方法的返回值,表示标签后面余下的JSP页面继续执行

SKIP_PAGE
doEndTag()方法的返回值,表示标签后面余下的JSP页面不被执行





抽象方法

void setPageContext(PageContext pc)
JSP容器实例化标签处理器后,调用setPageContext()方法将JSP页面的内置对象pageContext对象传递给标签处理器,标签处理器可以通过pageContext对象与JSP页面进行通信

void setParent(Tag t)
调用setPageContext()方法后,JSP容器会调用setParent()方法将当前标签的父标签处理器对象传递给当前标签处理器,如果当前标签没有父标签,则传递给setParent()方法的参数为null

Tag getParent()
返回当前标签的父标签处理器对象,如果当前标签没有父标签则返回null

int doStartTag()
当JSP容器解析到自定义标签的开始标签时,会调用doStartTag()方法,该方法可以返回EVAL_BODY_INCLUDE和SKIP_BODY两个常量,如果使用Tag的子接口BodyTag,还可以使用BodyTag.EVAL_BODY_BUFFERED常量。

int doEndTag() 当JSP容器解析到自定义标签的结束标签时,会调用doEndTag()方法,该方法可以返回EVAL_PAGE和SKIP_PAGE两个常量

void release()
JSP容器在标签处理器对象被作为垃圾回收之前调用release()方法,以便释放标签处理器所占用的资源

IterationTag接口


对标签体的内容进行重复处理

它继承自Tag接口,在Tag接口基础上新增了一个EVAL_BODY_AGAIN常量和一个doAfterBody()方法

EVAL_BODY_AGAIN常量

是doAfterBody()方法的返回值,如果doAfterBody()方法返回该常量,JSP容器会把标签体的内容重复执行一次。





doAfterBody()方法

JSP容器在每次执行完标签体后会调用doAfterBody()方法,该方法可以返回常量SKIP_BODY和EVAL_BODY_AGAIN。如果方法返回SKIP_BODY常量,JSP容器会去执行代表结束标签的doEndTag()方法,如果返回EVAL_BODY_AGAIN,则重复执行标签体。

BodyTag接口


对标签体的内容进行处理以后再向浏览器输出

它继承自IterationTag接口,在IterationTag接口基础上新增了两个方法和一个静态常量

EVAL_BODY_B9UFFERED常量





如果标签处理器类实现了BodyTag接口,它的doStartTag()方法除了可以返回SKIP_BODY和EVAL_BODY_INCLUDE常量之外,还可以返回EVAL_BODY_BUFFERED常量。

  当doStartTag()方法返回EVAL_BODY_BUFFERED常量时,JSP容器将会创建一个javax.servlet.jsp.tagext.BodyContent对象,使用该对象来执行标签体。





setBodyContent(BodyContent b)方法





当且仅当doStartTag()方法返回EVAL_BODY_BUFFERED常量时,JSP容器才会调用setBodyContent()方法,通过该方法将BodyContent对象传递给标签处理器类使用。





doInitBody()方法





JSP容器在调用setBodyContent()方法后会调用doInitBody()方法来完成一些初始化工作,该方法的调用在标签体执行之前。其中,最重要的是setBodyContent()方法。

BodyContent类


是JspWriter类的子类,它在JspWriter的基础上增加了一个用于存储数据的缓冲区,当调用BodyContent对象的方法写数据时,数据将被写入到BodyContent内部的缓冲区中。





当标签处理器类的doStartTag()方法返回EVAL_BODY_BUFFERED常量时,JSP容器会创建一个BodyContent对象,然后调用该对象的write()方法将标签体的内容写入BodyContent对象的缓冲区中。





在BodyContent类中定义了一些用于访问缓冲区内容的方法

String getString()
以字符串的形式返回BodyContent对象缓冲区中保存的数据

Reader getReader()
返回一个关联BodyContent对象缓冲区中数据的Reader对象,通过Reader对象可以读取缓冲区中的数据

void clearBody() 用于清空BodyContent对象缓冲区中的内容

JspWriter getEnclosingWriter()
用于返回BodyContent对象中关联的JspWriter对象。当JSP容器创建BodyContent对象后,PageContext对象中的“out”属性不再指向JSP的隐式对象,而是指向新创建的BodyContent对象。同时,在BodyContent对象中会用一个JspWriter类型的成员变量enclosingWriter记住原来的隐式对象,getEnclosingWriter()方法返回的就是原始的JSP隐式对象

writerOut(Writer out)
用于将BodyContent对象中的内容写入到指定的输出流

简单标签


实现SimpleTag接口的标签称为简单标签

由于传统标签在使用三个标签接口来完成不同的功能时,显得过于繁琐,不利于标签技术的推广。





SimpleTag接口与传统标签接口最大的区别在于:SimpleTag接口中只定义了一个用于处理标签逻辑的doTag方法,该方法用于取代传统标签接口中定义的doStartTag()、doEndTag()和doAfterBody()等方法。

  doTag()方法在JSP引擎执行自定义标签时调用,并且只被调用一次,那些使用传统标签接口所能完成的功能,都在doTag()方法体内完成。

简单标签API


SimpleTag接口


是所有简单标签处理器的父接口,它共定义了5个方法

void setJspContext(JspContext pc)
用于将JSP页面的内置对象pageContext对象传递给标签处理器,标签处理器可以通过pageContext对象与JSP页面进行通信。JSPContext类是PageContext类的父类,其中定义了一些不依赖于Servlet运行环境的方法,setJspContext()方法接收的参数类型为JspContext,是为了便于将简单标签扩展应用到非Servlet运行环境中

void setParent(JspTag parent)
用于将当前标签的父标签处理器对象传递给当前标签处理器,如果当前标签没有父标签,JSP容器不会调用这个方法

JspTag getParent()
返回当前标签的父标签处理器对象,如果当前标签没有父标签则返回null

void setJspBody(JspFragment jspBody)
用于把代表标签体的JspFragment对象传递给标签处理器对象

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

JspFragment类



它的实例对象代表JSP页面中一段JSP片段,但是这段JSP片段中不能包含JSP脚本元素





JSP容器在处理简单标签的标签体时,会把标签体内容用一个JspFragment对象表示,并调用标签处理器对象的setJspBody()方法将JspFragment对象传递给标签处理器对象,标签开发者可以根据需要调用JspFragment对象的方法来决定是否输出标签体、或者循环多次输出标签体等





两个方法

JspContext getJspContext()
用于返回代表调用页面的JspContext对象

void invoke(Writer out)
用于将标签体内容写入到指定的输出流对象out 中,如果调用该方法时传入的参数为null,JSP容器会将标签内容写入到JspContext.getOut()方法返回的输出流对象中。

SimpleTagSupport类



为了简化简单标签处理器的编写





该类实现了SimpleTag接口,它内部使用成员变量jspContext和jspBody引用了JSP容器传入的JspContext对象和JspFragment对象,并且提供了两个方法来返回这两个对象的引用

JspContext getJspContext()
用于返回代表调用页面的JspContext对象

JspFragment getJspFragment()
用于返回代表标签体的JspFragment对象







控制是否执行标签体内容

简单标签使用doTag()方法完成判断,如果用户登录,则显示用户名称,如果用户没有登录,则可以抛出javax.servlet.jsp.SkipPageException异常,用于通知JSP容器不再执行标签体内容,这等效于在传统标签的doEndTag()方法中返回SKIP_PAGE常量。

控制是否执行JSP页面的内容





假设一个网站要求只能通过本网站中的超链接来访问某些JSP页面时,如果直接访问这些JSP页面或者通过非本网站的超链接来访问这些JSP页面,那么被访问的JSP页面应该停止执行其中的内容,此现象称为防盗链。

简单标签的属性


如果多个JSP页面都需要防盗链功能,则需要将用户的访问请求重定向到不同的页面。

  这时,为了提高标签的灵活性和复用性,在JSP页面使用自定义标签时,可以通过设置属性为标签处理器传递参数信息。





例如,可以为<itcast:antiHotLinking />标签增加一个url属性,通过该属性指定重定向的页面

<itcast:antiHotLinking url = "/chapter09/index.html" />

    要想为自定义标签设置属性,通常需要完成两件任务,具体如下:

1、在标签处理器类中,为每一个属性定义对应的成员变量并定义setter()方法

自定义标签每个属性都必须按照JavaBean的属性定义方式,默认情况下,自定义标签的属性值为静态文本,当setter()方法的参数为String类型时,JSP容器会直接将属性值作为一个字符串传递给该方法。如果setter()方法中的参数类型为基本数据类型,JSP容器在调用setter()方法之前会先将属性值进行转换。

例如在JSP页面中使用了如下所示的标签:

<itcast:test attr = "123" />

而标签处理器类中定义的setter方法如下所示:

public void setAttr(int attr)

当JSP容器在处理attr属性时,会先调用Integer. value("123")方法将字符串“123”转换为整数123,之后再作为参数传递给标签处理器类的setAttr()方法。





2、在TLD文件中声明每个标签的属性信息

在TLD文件中,<tag>标签有一个子元素<attribute>用于描述自定义标签的属性,自定义标签的每个属性都必须要有一个对应的<attribute>元素

在<attribute>元素中还包含了一些子元素

description
用于描述属性的描述信息

name
用于指定属性的名称,属性名大小写敏感,且不能以jsp、_jsp、java、和sun开头

required
用于指定在JSP页面调用自定义标签时是否必须设置这个属性。其取值包括true和false,true表示必须设置,false表示设不设置均可。默认值为false

rtexprvalue
rtexprvalue是runtime expression value(运行时表达式)的简称,用于指定属性的值为静态还是动态。其取值包括true和false,true表示属性值可以为一个动态元素,比如一个脚本表达式<%=value%>,false表示属性值只能为静态文本值,比如“abc”。默认值为false

type
用于指定属性值的类型





<name>子元素用于指定属性的名称,其值必须进行设置,而且属性名称一定要和jsp页面中自定义标签属性名一致,其它的子元素则可以设置也可以不用设置。