Java精选笔记_自定义标签

自定义标签

自定义标签入门


什么是自定义标签


自定义标签可以有效地将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页面中自定义标签属性名一致,其它的子元素则可以设置也可以不用设置。
原文地址:https://www.cnblogs.com/justdoitba/p/7582120.html