在J2EE项目中,JSP页面常常通过在静态页面模板中嵌入scriptlets来插入动态的内容。然而,随着复杂程序的增加,JSP页面也变得难于管理。 虽然用这种方法开发小形项目唾手可得,但是scriptlets仍然要面对下面的不利情况:
Scriptlet难于阅读和修改。带有Scriptlets的JSP页面混合了两种语言,这使得阅读和维护变得很困难。
Scriptlets鼓励了将数据表现和逻辑处理的混合 。JSP页面主要功能是数据表现而不是逻辑处理。逻辑处理属于Java类的工作,他们应该由程序员维护并能够重用。
Scriptlets不能够被重用。当scriptlets被重用时,常常是鼓励拷贝-粘贴来达到重用的效果,这是一种危险的维护方法。每一次你拷贝-粘贴scriptlets时,将有更多行多余的代码需要维护。
Scriptlets的参数很难进行确定传递. 无论如何,绝大数人简单的拷贝,粘贴、编辑或者类似的增加,使得大部份的多余的代码需要更多的维护。
与其创建充满了scriptlets的巨大的JSP页面,不如考虑使用用户自定义标签。用户自定义标签允许你创建、在JSP中使用你自己定义的类HTML标签。每当JSP引擎遇到用户自定义标签时,就会自动查找标签处理类,并自动调用他。页面中的自定义标签将会被票签处理类的输出所代替。这就使得JSP页面不用直接在页面写Java代码,就可以指定生成动态的内容的。
用户自定义标签为你的网页设计提供了N种好处:他提高了页面代码的可读性。页面设计人员而不是程序员,能够使用很比使用Scriptlets更容易的使用标签。维护代码的程序员也只需个性标签库面不是JSP页面,这样他就不要再冒着破坏页面美观的风险。 在使用标签的每一处,增强或者固定的标签改变了标签了的表现。标签比Scriptlets更容易确定参数,因为他会被 作为一种属性或者在标签体内被传达。最后,标签比Scriptlets高度的可重用性,因为你可以创建共享的、可重用的自定义标签库。 JSTL提供的就是这种标准的自定义标签集。
让我们通过看一个简单的JSP页面例子,来看看如何使用自定义标签。 下面这个页面使用scriptlet来得到数据:
<%@ page import="java.text.SimpleDateFormat" %>
<%@ page import="java.util.Date" %>
。如果你想要在每一个页面上显示日期,那么你只能拷贝粘贴这段代码到项目中的每一个页面。如果你这么做,那么你要维护的不仅仅是这段代码的原始拷贝,而是你粘贴的每一个拷贝。 如果这段代码出现在多个页面,那么改变一下时间格式将会占用你的很多时间。
下面这段是一个非常清洁的JSP页面。在这里,Java代码被从scriptlet中移出放到了自定义标签中:
<%@ taglib uri="/WEB-INF/taglib.tld" prefix="mytags" %>
个标签时会调用doStartTag方法。这个方法和第1个版本JSP页面的scriptlet处理了相同的事。他将结果写回给JspWriter,结果包括了先存储的PageContext.所有写给JspWriter的内容,会被直接嵌入到响应页面。 注意doStartTag只能抛出JspException异常。如果发生写失败,那么原始IOException会被转化成一个JspException重新抛出。这个方法会返回SKIP_BODY,他告诉JSP容器抛弃标签内容。
标签处理器最后的两个方法是setFormat和getFormat。机敏的读者应该已经知道了他作用。 他被网页容器用来设置标签属性值(后面进行更详细的讨论)。在这儿他们被用来设定日期格式属性和输出日期格式属性。
标签定义:TLD文件
一个标签库描述符文件, 或者TLD文件, 是一个XML文件。他用来描述标签库中的标签。以下是DateTag标签的描述文件。
PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"
"https://www.doczj.com/doc/1a11301755.html,/dtd/web-jsptaglibrary_1_2.dtd">
这些描述符提供了这个标签库的信息。他还提供了每一个标签的如标签名、处理器的类和标签描述的信息。这个文件被用来替换WEB-INF目录下的WAR文件,为JSP页面使用这些标签提供参照。
增加属性
Notice that the TLD file shown above defines an attribute called format. This is the string passed to SimpleDateFormat to control how the date is printed. If a format attribute is present on a date tag, the JSP engine calls setFormat on the handler class. Otherwise, the handler class uses a default format. Attributes provide a great deal of customizability to custom tags. For example, the following JSP page uses the format attribute to format the date in several different ways on the same page:
注意上面这个TLD文件显示的属性调用格式 。这是一个字符串被传递给SimpleDateFormat,来控制如何显示日期。如果一个日期格式被提供给日期标签,那么JSP引擎将调用控制器类中的setFormat方法 ,否则,标签处理器将使用默认格式
。属性为用户定义标签提供了大量可定制属性。例如以下这个例子,在同一个页面的, 用几种不同的格式属性来格式化时间。
<%@ taglib uri="/WEB-INF/taglib.tld" prefix="mytags" %>
tters()—>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文件:
"https://www.doczj.com/doc/1a11301755.html,/dtd/web-jsptaglibrary_1_2.dtd">
让我们更详细地分析一下这些标签:
TLD 文件的根元素是.它描述了一个标签库 —— 即一组标签/标签处理程序对。
empty:表示不能有标签体
JSP:标签体可以是任意一种JSP页面元素
scriptless:不能是JSP页面元素
tagdependent:由标签处理器去处理.jsp引擎不处理标签体,
给自定
义标签定义属性:
先在java文件中定义属性和set方法.
然后在tld文件中加入:
使用动态属性:在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页面
在简单标签中定义属性:
等同于
使用下面这种方法时,要注意:如果标签有标签体,不能随便写了,应该这样:
这里写你的标签体
标签库的描述和部署:
jsp1.2:tld文件用的是dtd文件
jsp2.0:用的是xsd文件.
1. 可以<%@ taglib="" prefix=""%>
2. 在web.xml文件中
给标签库文件指定一个URI,任何有意义的名字都可以,
但是通常以”http://”打头
app>
3.也可以把标签文件打成jar包放在web应用程序的lib目录下.
自定义标签的功能先介绍到这里,在下面的blog中再加上几个小例子.
=======================================================
JSP页面中的自定义标签
减小字体 增大字体 作者:佚名 来源:本站整理 发布时间:2005-7-1 12:12:50
JSP页面中的自定义标签
JSP页面中的自定义标签
Stephanie Bodoff
用于调用Javaean组件中的操作和执行请求分派的标准JSP标签简化了JSP页面的开发和维护。JSP技术还提供了在自定义标签中封装其他动态功能的机制,这种自定标签是JSP语言的扩展。自定义标签通常是以标签库的形式出现的,它定义了一组相关的自定义标签,并包含实现这些标签的对象。
可以由自定义标签执行的任务包括对隐式对象的操作、处理表单、访问数据库和其他企业级服务,如电子邮件和目录、以及执行流程控制。JSP标签库是由精通Java编程语言和对访问数据库和其他服务非常熟悉的开发人员创建的,使用这些标签,Web应用程序开发人员就可以把注意力放到内容的呈现上,而不用费心考虑如何访问企业级服务。就像鼓励将库开发人员和库使用人员的工作分开一样,自定义标签通过封装反复执行的任务使它们可以在多个应用程序中重复使用,从而提高了生产率。
JSP技术社区给予标签库非常高的重视。有关标签库的信息和一些免费的库的地址,参见
https://www.doczj.com/doc/1a11301755.html,/products/jsp/taglibraries.html
什么是自定义标签?
自定义标签是用户定义的JSP语言元素。当包含自定义标签的JSP页面转换为servlet时,这个标签就转换为一个名为tag handler的对象上的操作。之后当JSP页面的servlet执行时,Web容器就调用这些操作。
自定义标签有丰富的功能。它们可以
· 通过从调用页面传递的属性进行定制。
· 访问JSP页面可以使用的所有对象。
· 修改由调用页面生成的响应。
· 彼此通信。可以创建并初始化JavaBean组件、在一个标签中创建引用该bean的变量、再在另一个标签中使用这个bean。
· 彼此嵌套,可以在JSP页面中实现复杂的交互交互。
JSP页面示例
本章描述使用和定义标签所涉及的任务。本章用改写了的、在JSP页面示例中讨论的JSP版本的Duke’s Bookstore应用程序的部分演示这些任务,所做的改写利用了两个标签库的优点:Struts和tutorial-template。本章的第三节示例详细描述了两个标签:Strutst中的iterate和tutorial-template标签库中的一组标签。
Struts标签库提供了构建实现模型-视图-控制设计模式的国际化Web应用程序的框架。Struts包括完整的一组自定义工具标签,用于处理:
· HTML 表单
· 模板
· Jav
aBeans组件
· 逻辑处理
Duke's Bookstore应用程序使用Struts bean和logic子库中的标签。
Tutorial-template标签库定义了一组用于创建应用程序模板的标签。模板是带有占位符的JSP页面,这些占位符需要在每一屏幕中改变。每一个占位符称为模板的参数。例如,一个简单的模板可能包括在生成的屏幕上方的title参数,和一个JSP页面作为屏幕的定制内容的body参数。模板是用一组嵌入的标签创建的——definition、screen和parameter——它们用于构建Duke's Bookstore的屏幕定义表,并用insert标签将参数从表中插入屏幕。
图16-1显示了通过Duke's Bookstore Web组件的请求流程:
· template.jsp, template.jsp确定每一屏幕的结构。它使用insert标签用子组件组成屏幕。
· screendefinitions.jsp,它定义了每一屏幕使用的子组件。所有屏幕都有相同的横幅,但是标题和正文不同(由表15-1中的JSP页面列所指定)。
· Dispatcher,这是一个servlet,它处理请求并转发给template.jsp。
图16-1 通过Duke's Bookstore组件的请求流程
Duke's Bookstore应用程序的源代码位于在解压缩教程包(见运行示例)时生成的docs/tutorial/examples/web/bookstore3目录中。要编译、部署并运行这个例子,你需要:
1. 从以下地址下载Struts version 1.0.2
https://www.doczj.com/doc/1a11301755.html,/builds/jakarta-struts/release/v1.0.2/
2. 解压缩Struts并将struts-bean.tld、struts-logic.tld和struts.jar从jakarta-struts-1.0/lib拷贝到
3. 在终端窗口,进入
4. 运行ant build。Build目标会进行所有必要的编译并将文件拷贝到
5. 确保已经启动了Tomcat。
6. 运行ant install。Install目标通知Tomcat 已经有了内容。
7. 如果还没有做的话,就启动PointBas数据库服务器并加入数据(见从Web应用程序中访问数据库)。
8. 打开书店URL http://localhost:8080/bookstore3/enter。
有关诊断常见问题的帮助见常见问题及其解决方案和故障排除。
使用标签
本节描述JSP页面如何使用标签,并介绍不同类型的标签。
要使用标签,页面编写者必须做以下两件事:
· 声明包含标签的标签库
· 让标签库实现对于Web应用程序可用
声明标签库
通过在使用任何自定义标签之前,将taglib指令加入页面中声明JSP页面将使用在标签库中定义的标签:
<%@ taglib uri="/WEB-INF/tutorial-template.tld" prefix="tt" %>
uri属性表示唯一标识标签库描述符(TLD)的URI,在标签库描述符中描述了uri。这个URI可以是直接或者非直接的。prefix属性定义了区分指定标签库所定义的标签与其他标签库提供的标签的前缀。
标签库描述符文件名必须有扩展名.tld。TLD文件储存在WAR的WEB-INF目录中,或者在WEB-INF的子目录中。可以直接或者间接引用TLD。
下面taglib指令直接引用一个TLD文件名:
<%@ taglib uri="/WEB-INF/tutorial-template.tld" prefix="tt" %>
这个taglib指令使用一个短的逻辑名间接引用TLD:
<%@ taglib uri="/tutorial-template" prefix="tt" %>
在Web应用程序部署描述符中将逻辑名映射到一个绝对位置。要将逻辑名/tutorial-template映射为绝对位置/WEB-INF/tutorial-template.tld,在web.xml中添加元素taglib:
/WEB-INF/tutorial-template.tld
让标签库实现可用
可以以两种方式让标签库实现对Web应用程序可用。实现了标签handler的类可以以非打包的形式储存在Web应用程序的WEB-INF/classes子目录中。另一种方法是,如果以JAR的形式发布库,就将它储存在Web应用程序的WEB-INF/lib目录中。在多个应用程序中共享的标签库储存在Java WSDP的
标签类型
JSP自定义标签是用XML语法编写的。它们有一个开始标签和结束标签,可能还有正文:
body
不带正文的自定义标签如下表示:
简单标签
一个简单标签没有正文,也没有属性:
带属性的标签
自定义标签可以带有属性。属性列在开始标签中,语法为attr="value"。像用参数定制方法的行为一样,属性值用于定制自定义标签的行为。在标签库描述符中指定标签属性的类型(见带属性的标签)。
可以用一个常量或者运行时表达式设置属性值。常量和运行时表达式与属性类型之间的转换过程遵循在设置JavaBean组件属性中描述的JavaBean组件属性规则。
Struts logic:present标签的属性决定是否对标签的正文进行判断。在下面的例子中,一个属性指定需要一个名为的参数Clear:
Duke's Bookstore应用程序页面catalog.jsp使用了运行时表达式设置属性的值,它决定Struts logic:iterate标签要枚举哪几本书。
带正文的标签
自定义标签可以包含自定义和核心标签、脚本元素、HTML文本和开始与结束标签之间的、依赖于标签的正文内容。
在下面的例子中,Duke's Bookstore应用程序页面showcart.jsp使用Struts logic:present标签清除购物车,并且如果请求包含一个名为Clear的参数就打印一个消息。
<% cart.clear(); %>
You just cleared your shopping cart!
选择用属性或者正文传递信息
正如最后两节中所展示的,可以将给定的数据作为标签的属性或者标签的正文传递。一般来说,任何简单字符串或者可以由对简单表达式判断而生成的数据最好作为属性传递。
定义脚本变量的标签
自定义标签可以定义可在页面中的脚本中使用的变量。下面的例子展示了如何定义并使用包含一个从JNDI查询中返回的对象的脚本变量。这种对象的例子包括企业bean、事务、数据库、环境项等等:
name="java:comp/UserTransaction" />
<% tx.b
在Duke's Bookstore应用程序中,有几个页面使用了Struts的面向bean的标签以定义脚本变量。例如,bookdetails.jsp使用了bean:parameter标签以创建脚本变量bookId并设置它并将它设置为请求参数bookId的值。jsp:setProperty语句还设置bookDB对象的bookId属性为请求参数bookId的值。bean:define标签提取书店数据库bookDetails属性bookDetails的值并将结果定义为脚本变量book:
操作标签
自定义标签可以通过共享对象彼此合作。
在下面的例子中,tag1创建了一个名为obj1的对象,再由tag2返回这个对象。
在下面的例子中,由一组嵌套标签中的外围标签创建的对象对于所有内部标签都是可用的。因为没有为对象命名,所以可以减少潜在的命名冲突。这个例子展示在JSP页面中一组协作的嵌入标签会是什么样子的。
Duke's Bookstore页面template.jsp使用了一组协作标签定义应用程序的屏幕。在模板标签库中描述了这些标签。
定义标签
要定义标签,需要:
· 为该标签开发一个tag handler和helper类
· 在标签库描述符中声明这个标签
本节描述标签handler和TLD的属性,并解释如何为在前面几节中介绍的标签开发tag handler和库描述符元素。
标签handler
标签handler是由Web容器调用的一个对象,用于执行带有自定义标签的JSP页面时对这个标签进行判断。标签handler必须实现Tag或者BodyTag接口。接口可以用于接受现有Java对象并使它成为标签handler。对于新创建的处理器,可以用TagSupport和BodyTagSupport类作为基类。这些类和接口包含在javax.servlet.jsp.tagext包中。
JSP页面的servlet在对标签处理的不同阶段调用由Tag和BodyTag接口定义的标签handler。遇到自定义标签的开始标签时,JSP页面的servlet调用方法以初始化相应的handler,然
后调用handler的doStartTag方法。遇到自定义标签的结束标签时,调用处理器的doEndTag方法。在标签handler需要与标签的正文交互时调用其他方法,见带正文的标签。为了提供标签handler的实现,必须实现在处理标签的不同阶段调用的方法,在表16-1中汇总了这些方法。
表16-1标签handler方法
标签handler类型
方法
简单
doStartTag, doEndTag, release
属性
doStartTag, doEndTag, set/getAttribute1...N, release
正文、判断且无交互
doStartTag, doEndTag, release
正文、迭代判断
doStartTag, doAfterBody, doEndTag, release
正文、交互
doStartTag, doEndTag, release, doInitBody, doAfterBody, release
标签handler可以使用一个能让它得以与JSP页面通信的API。到API的入口点是页面上下文对象(javax.servlet.jsp.PageContext),通过它标签handler可以获取JSP页面能够访问的所有其他隐式对象(请求、会话和应用程序)。
隐式对象可以有与其相关联的命名属性。可以用[set|get]Attribute方法访问这种属性。
如果标签是嵌入的,标签handler还可以访问与外围标签关联的handler称为parent)。
一组相关的标签handler类(标签库)一般是打包的且作为JAR文档部署。
标签库描述符
标签库描述符(TLD)是一个描述标签库的XML文档。TLD包含有关整个库以及库中包含的每一个标签的信息。Web容器用TLD验证标签,JSP页面开发工具也使用TLD。
TLD文件名必须有扩展名.tld。TLD文件也储存在WAR文件的WEB-INF目录中或者在WEB-INF的子目录中。
TLD必须以指定XML的版本和文档类型定义(DTD)的XML文档序言(prolog)开始。
Tomcat支持版本 1.1和1.2的DTD。不过,本章所讨论的是1.2版本,因为在开发的所有标签库中都应该使用最新的版本。模板库TLDtutorial-template.tld符合版本1.2。Struts库TLD符合版本1.1的DTD,它的元素要少,且其中一些元素使用了稍微不同的名字。
TLD的根是taglib元素。表16-2中列出了taglib的子元素:
表16-2 taglib子子元素
元素
说明
tlib-version
标签库的版本
jsp-version
这个标签库要求的JSP规范版本
short-name
JSP页面编写工具可以用来创建助记名的可选名字
uri
唯一标识该标签库的的URI
display-name
将由工具显示的可选名
small-icon
将由工具使用的可选小图标
large-icon
可被工具使用的可选大图标
description
可选的标签特定信息
listener
见listener元素
tag
见tag元素
listener元素
标签库可以指定一些事件监听器类(见处理Servlet生命周期事件)。这些监听器在TLD中作为li
stener元素列出,Web容器将初始化监听器类并以类似在WAR级定义的监听器的方式注册它们。与WAR级监听器不同,这里没有指定标签库监听器注册的顺序。listener元素的唯一子元素是listener-class元素,它必须包含监听类的完全限定名。
tag元素
库中的每一个标签都由给出其名字和其标签handler的类、在由标签创建的脚本变量上的信息以及标签属性上的信息描述。脚本变量信息可以在TLD中直接给出,也可以通过tag extra info类给出(见定义脚本变量的标签)。每一个属性声明包含指明属性是否是必需的、其值是否可以由请求时表达式确定以及属性类型的内容(见属性元素)。
在tag元素中的TLD中指定标签。在表16-3中出了tag的子元素:
表16-3 标签子元素
元素
说明
name
唯一标签名
tag-class
标签handler类的完全限定名
tei-class
javax.servlet.jsp.tagext.TagExtraInfo的可选子类。见提供有关脚本变量的信息。
body-content
正文内容类型。见body-conten元素和 body-content元素。
display-name
由工具显示的可选名
small-icon
可以由工具使用的小图标
large-icon
可以由工具使用的大图标
description
可选的标签特定的信息
variable
可选的脚本变量信息。见提供有关脚本变量的信息。
attribute
标签属性信息。见Attribute 元素。
下面几节描述开发在标签类型中介绍的每一种类型的标签所需要的方法和TLD。
简单标签
标签handler
简单标签的handler必须实现Tag接口的doStartTag和doEndTag方法。在遇到开始标签时调用doStartTag方法。因为简单标签没有正文,所以这个方法返回SKIP_BODY。在遇到结束标签时调用doEndTag方法。如果要对页面的其他部分进行判断,则doEndTag方法需要返回EVAL_PAGE,否则,它就返回SKIP_PAGE。
在第一节讨论的简单标签
由下列标签handler实现:
public SimpleTag extends TagSupport {
public int doStartTag() throws JspException {
try { pageContext.getOut().print("Hello.");
} catch (Exception ex) {
throw new JspTagException("SimpleTag: " +
ex.getMessage()); }
return SKIP_BODY; }
public int doEndTag() {
return EVAL_PAGE;
}
}
body-content元素
没有正文的标签必须用body-content元素声明它们的正文内容是空的:
JSP页面中的自定义标签