Peanut's Book Shelf » 日志 » Way to Webdev:) Head First Servlet & JSP Chap10: 定制标记开发
Way to Webdev:) Head First Servlet & JSP Chap10: 定制标记开发
Mystra 发表于 2008-04-20 22:13:27
标记文件:它的出现是因为,我们认为目前的c:import和jsp:include都不够好,只是向里面传递一些自定义参数,没有必要新建请求属性
建立方式
1. 取一个包含文件,如"Header.jsp",将其命名为Header.tag
2. 将其放入WEB-INF目录下一个名叫tags的目录中
3. 在jsp中放一个taglib指令 :
<%@ taglib prefix="myTags" tagdir="/WEB-INF/tags" %>
<html><body>
<myTags:Header /> <!-- Header就是标记文件名。但是要去掉.tag扩展名 -->
</body></html>
** 以前的jsp:param实际上是设了一个请求参数。而这里我们要发参数,做的就更加简单些
<myTags:Header subTitle="We take the String here" />
现在在标记文件里只要<em><strong>${subTitle}</strong></em>就可以了,不用写${param.subTitle}
** 这样传递的参数具有标记作用域。出了自定义标记就没有用了。
** 重要:标记文件属性不在TLD中声明,而在标记文件内使用attribute指令指定
<!-- Header.tag -->
<%@ attribute name="subTitle" required="true" rtexprvalue="true" %>
<em><strong>${subTitle}</strong></em>
** 如果使用时没加必要的参数,会抛一个异常
** 如果属性值很大...放到开始标记里很难看,这时我们可以将内容放在体中
<!-- Header.tag -->
<em><strong><jsp:dobody/></strong></em>
<!-- JSP -->
<myTags:Header>
A very veeeeeeeery long sentence
</myTags:Header>
tag指令:专用于标记文件,类似于普通jsp的page指令,但有一个独一无二的属性body-content
<!-- Header.tag -->
<%@ tag body-content="tagdependent" %> <!-- 这里可选的值还有empty和scriptless -->
** 标记文件体中不能出现脚本,所以也不可能有JSP可以选
** 请确定你看懂了上一行。。这是说体中不能有,标记文件里是可以有脚本的!!
** 标记文件如果直接放在WEB-INF/tags及其子目录中,则不需要TLD.如果放在某个jar中的META_INF/tags中,则需要TLD
** 标记文件一样可以访问JSP的隐式对象
标记文件的TLD项只描述具体标记文件的位置,而没有body-content,attribute等属性(这些都在标记文件中)
<taglib ....>
<tlib-version>1.0</tlib-version>
<uri>myTagLibrary</uri>
<tag-file>
<name>Header</name>
<path>/META-INF/tags/Header.tag</path>
</tag-file>
</taglib>
如果标记文件还不够(怎么可能够-_-功能上又没什么扩充),我们就来看看自定义标记。。。这下我们的能力总算被扩展到了极限!!
** 自定义标记也分为简单标记处理器和传统标记处理器
建立简单标记的过程:
1. 编写一个扩展SimpleTagSupport的类
package foo;
import javax.servlet.jsp.tagext.SimpleTagSupport;
public class SimpleTagTest1 extends SimpleTagSupport {
...
}
2. 实现doTag()方法
public void doTag() throws JspException,IOExcpetion {
getJspContext().getOut().print("Sample Output");
}
3. 为标记创建一个TLD
<taglib ....>
<tlib-version>1.2</tlib-version>
<tag>
<description>worst use of a custom tag</description>
<name>simple1</name>
<tag-class>foo.SimpleTagTest1</tag-class>
<body-content>empty</body-content> <!-- 可选项有scriptless,tagdependent和jsp -->
</tag>
</taglib>
4. 编写一个使用标记的JSP
<%@ taglib prefix="myTags" uri="simpleTags" %>
<html><body>
<myTags:simple1/>
</body></html>
** 在doTag()中调用getJspBody.invoke(null)可以将体内容输出到响应(里面接受的是一个writer.如果想将体序列化出来看看,可以填入其它的writer)
** 类的派生体系为,接口SimpleJspTag派生JspTag,而类SimpleTagSupport实现了SimpleJspTag,但是doTag()什么也不做
简单标记处理器的生命期
1. 加载类
2. 实例化类
3. 调用setJspContext方法
4. 如果标记是被嵌套的,则调用setParent方法
以上是容器自动做的。下面是我们自己要做的
5. 如果标记有属性,要调用属性的设置方法
6. 如果标记的<body-content>未声明为empty,而且标记有体,则调用setJspBody(JspFragment)方法
7. 调用doTag()方法
专题:如何反复执行体?
<!-- JSP标记调用 -->
<table>
<myTags:simple4>
<tr><td>${movie}</td></tr>
</myTags:simple4>
</table>
// 标记处理器的doTag
String[] movies = {"Movie1", "Movie2"} ;
public void doTag() throws JspException, IOExcpetion {
for(int i = 0 ;i < movies.length; i++)
{
getJspContext().setAttribute("movie", movies[i]);
getJspBody().invoke(null); <!-- 不错...多次调用了,有点动态的效果 -->
}
}
专题:有属性的简单标记
要做到以下几点:
1. 首先要在TLD中声明此tag需要一个什么attribute...
2. 然后要为每个属性提供一个bean式的设置方法
** 在doTag中throw SkipPageException; 可以让页面的余下部分不再工作,但是已经到响应里面的内容还是会显示出来的
** 如果在一个被包含页面调用标记..例如A页面包含了B页面,B页面调用了标记。这是标记中抛出SkipPageException,那么B页面剩下的部分不显示了,但是A页面剩下的内容还是会显示的!!
** SimpleTag处理器绝对不会重用。每个标记处理器实例只负责一次调用
** SimpleTag处理器的属性可以是任意类型
** 如果标记声明为有体,但实际的标记调用体为空,那么setJspBody不会被调用。
传统标记处理器不从SimpleTagSupport派生。
一般,如果不需要对体做迭代处理,派生TagSupport,否则派生BodyTagSupport
TagSupport的几个方法:
doStartTag : 它在体计算之前调用
** doStartTag是有返回值的。通过返回值控制流程
** doStartTag的返回值可能有 : SKIP_BODY(忽略体) , EVAL_BODY_INCLUDE(计算体)
doEndTag : 在体计算之后调用
** doEndTag的返回值可能有 : EVAL_PAGE(继续计算余下的页面), SKIP_PAGE(不计算剩下页面,相当于简单标记处理器抛出一个SkipPageException)
doAfterTag : 在每次计算体之后调用
** 可能的返回值 : EVAL_BODY_AGAIN(再次计算体), SKIP_BODY(转去执行doEndTag)
** 注意,只有派生了IterationTagSupport才能返回EVAL_BODY_AGAIN
如果确实需要把体拿出来看看...
可以实现派生BodyTagSupport。
这样,doStartTag多了一个可选返回值EVAL_BODY_BUFFERED,如果返回这个,流程将变化,在计算体之前会先执行setBodyContent,再执行doInitBody,这两个都是BodyTagSupport的方法
** 通过传统标记的getParent可以得到父标签,可以沿父标签轴一路向上,直到返回null(getparent不会抛出异常)
** 路径上可以包括简单标记
** 没有getChild之类的方法。如果父标记要得到子标记的属性,只有用子标记调用父标记的某个set方法,将属性“送上门来”
** TagSupport的静态方法findAncestorWithClass可以接受两个参数(Tag,class)来在Tag的父标签中为class的标签
