Java Web之自定义JSP标签 write:2022-5-7
上文:Java Web之使用过滤器。 前面我们学习了JSP自带的标签:Java web之JSP技术详解,但自带标签数量有限,从JSP1.1开始出现了自定义JSP标签技术,允许用户创建客户化的标签,从而完成特定需求的功能。
1. 理解自定义JSP标签的作用
自定义JSP标签技术是在JSP 1.1版本中才出现的,它支持用户在JSP文件中自定义标签,这样可以使JSP代码更加简洁。 自定义标签是可重用的 自定义标签能处理复杂的逻辑运算和事务,或者定义JSP网页的输出内容和格式。
2. 掌握创建并运用自定义JSP标签的步骤
因为自定义的JSP标签可以被重复使用,因此创建自定义JSP标签和在JSP文件中使用自定义JSP标签是独立的;
- 创建自定义JSP标签
(1)创建标签的处理类(决定标签完成的功能) (2)创建XML格式的标签库描述文件 - 在JSP文件中使用自定义JSP标签
(1)在web.xml文件中声明所引用的自定义标签所在的标签库(标签库:组织和管理一组功能相关的标签) (2)在JSP文件中引入标签库,然后插入标签,例如:< mm:hello/>
3. 了解JSP Tag API
3.1 标签的处理类
Servlet容器执行JSP文件时,如果遇到自定义标签,就会调用这个标签的处理类。 标签处理类必须扩展以下两个类之一: javax.servlet.jsp.TagSupport或者javax.servlet.jsp.BodyTagSupport 下面主要结束如何扩展TagSupport类
3.2 TagSupport类
- TagSupport类的主要方法
doStartTag():Servlet容器遇到自定义标签的起始标志(即:<)时调用该方法 doEndTag():Servlet容器遇到自定义标签的结束标志(即:/>)时调用该方法 setValue(String k,Object o):在标签处理类中设置key/value getValue(String k): 在标签处理类中根据参数key返回匹配的value removeValue(String k):在标签处理类中删除key/value - TagSupport类的两个重要属性
(1)两个属性 parent:引用嵌套了当前标签的上层标签(父标签)的处理类对象 pageContext:引用当前JSP页面的javax.servlet.jsp.PageContext对象 (2)与属性对应的方法 setPageContext(PageContext pc):设置PageContext对象,该方法由Servlet容器在调用doStartTag或doEndTag方法前调用 setParent(Tag t):设置嵌套了当前标签的上层标签的处理类,该方法由Servlet容器在调用doStartTag()方法前调用 getParent():返回嵌套了当前标签的上层标签的处理类对象
Servlet容器在调用doStartTag() 方法前,会先调用setPageContext()和setParent()方法,设置pageContext和parent属性。 parent属性是private类型的,在doStartTag()或doEndTag()方法中可以通过getParent()方法获取上层父标签的处理类对象; 在TagSupport类中定义了protected类型的pageContext成员变量,因此在标签处理类中可以直接访问pageContext变量。
3.3 PageContext类
TagSupport类的两个重要属性之一:PageContext类 PageContext类本身由Servlet容器创建,它是Servlet容器为JSP页面提供的得力助手,在JSP页面的自定义标签也可以使用这个得力助手;
PageContext类提供了保存和访问Web应用的各个范围内的共享数据的方法:
public void setAttribute(String name, Object value, int scope)
public Object getAttribute(String name, int scope)
其中,scope参数用来指定属性存在的范围,它的可选值包括: PageContext.PAGE_SCOPE PageContext.REQUEST_SCOPE PageContext.SESSION_SCOPE PageContext.APPLICATION_SCOPE 例如:
pageContext.setAttribute(“username”,”Tom”, PageContext.SESSION_SCOPE);
String username=(String)pageContext.getAttribute(“username”, PageContext.SESSION_SCOPE);
3.4 doStartTag()方法的返回值
TagSupport类的主要方法之一:doStartTag():Servlet容器遇到自定义标签的起始标志(即:<)时调用该方法
doStartTag()方法返回一个整数值,用来决定程序的后续流程。它有两个可选值: Tag.SKIP_BODY表示标签之间的内容被忽略。 Tag.EVAL_BODY_INCLUDE表示标签之间的内容被正常执行。
例如对于以下代码: < prefix: Mytag> Hello …… …… < /prefix:Mytag>
假若< Mytag>的doStartTag()方法返回Tag.SKIP_BODY,”Hello”字符串不会显示在网页上;若返回Tag.EVAL_BODY_INCLUDE,“Hello” 字符串将显示在网页上。
3.5 doEndTag()方法的返回值
TagSupport类的主要方法之一:doEndTag():Servlet容器遇到自定义标签的结束标志(即:/>)时调用该方法
doEndTag()方法也返回一个整数值,用来决定程序后续流程。它有两个可选值: Tag.SKIP_PAGE:表示立刻停止执行JSP网页,网页上未处理的静态内容和JSP程序代码均被忽略,任何已有的输出内容立刻返回到客户的浏览器上。 Tag.EVAL_PAGE:表示按正常的流程继续执行JSP网页。
3.6 用户自定义的标签属性
在标签中还能包含自定义的属性,例如: < prefix:mytag username=“Tom"> …… …… < /prefix:mytag>
在标签处理类中应该将这个属性作为成员变量,并且分别提供设置和读取属性的方法; 假定以上username为String类型,可以定义如下方法: private String username; public void setUsername(String value){ this.username=value; } public String getUsername(){ return username; }
4. Servlet容器处理自定义标签的流程
4.1 范例1:创建hello标签
定义一个名为mytaglib的标签库,它包含一个简单的hello标签,这个标签能够将JSP页面中所有的< mm:hello/>解析为字符串“hello”。 hello标签的处理类HelloTag
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspTagException;
import javax.servlet.jsp.tagext.TagSupport;
public class HelloTag extends TagSupport{
public int doEndTag() throws JspException{
try{
pageContext.getOut().print("Hello");
}catch (Exception e) {
throw new JspTagException(e.getMessage());
}
return EVAL_PAGE;
}
}
创建hello标签的标签库的描述文件 创建Tag Library的描述文件mytaglib.tld文件,在这个文件中定义mytaglib标签库和hello标签。这个文件存放位置为/WEB-INF/mytaglib.tld。
<taglib>
<tlib-version>1.0</tlib-version>//标签库版本
<jsp-version>1.1</jsp-version>//jsp版本
<short-name>mytaglib</short-name>//标签库默认的前缀名
<uri>/mytaglib</uri>//标签库唯一的访问标识符
<tag>//定义一个标签
<name>hello</name>//标签名
<tag-class>mypack.HelloTag</tag-class>//标签处理类
<body-content>empty</body-content>//标签主体的类型
<info>Just Says Hello</info>//标签的说明信息
</tag>
</taglib>
如果某个web应用需要使用名为mytaglib的标签库中的标签,就需要在web应用中加入标签库的描述文件mytaglib.tld文件:把mytaglib.tld文件放在/WEB-INF下,目录/WEB-INF/mytaglib.tld; 在web.xml文件中加入< taglib>元素
<web>
……
<taglib>//声明web应用引用的标签库
<taglib-uri>/mytaglib</taglib-uri>//为标签库映射唯一一个标识符,根据标识符引用标签库
<taglib-location>/WEB-INF/mytaglib.tld</taglib-location>//标签库tld文件存放位置
</taglib>
<web>
在JSP文件中加入hello标签 (1)在hellowithtag1.jsp中加入引用mytaglib的taglib指令:<%@ taglib uri=“/mytaglib” prefix=“mm” %> 以上taglib指令中,prefix属性用来指定引用mytaglib标签库时的前缀(所有以mm开头的标签都来自mytaglib标签库)。
(2)在hellowithtag1.jsp文件中插入hello标签: < b>< mm:hello/> : <%= request.getParameter(“username") %> 访问hellowithtag1.jsp: http://localhost:8080/hellowithtag1.jsp?username=weiqin
4.2 范例2:创建message标签
创建一个能替换helloapp应用中JSP网页的静态文本的标签,这个标签名为message,它放在mytaglib标签库中。 在hellowithtag2.jsp文件中使用message标签的代码如下: < b>< mm:message key=“hello.hello” /> : <%= request.getAttribute(“username") %>< /b> 当客户访问hello.jsp网页时,message标签的处理类会根据属性key的值从一个文本文件中找到与key匹配的字符串。假定这个字符串为“Hello”,然后将这个字符串输出到网页上。 创建包含JSP网页静态文本的文件 首先将创建包含JSP网页静态文本的文件,这些文本以key=value的形式存放,这个文件名为messageresource.properties: 在Web应用启动时装载静态文本 尽管装载静态文本的任务可以直接由标签处理类来完成,但是把初始化的操作安排在Web应用启动时完成,这更符合Web编程的规范。 在本例中,由LoadServlet类的init方法负责从静态文本文件中读取静态文本,然后把它们装载到Properties对象中,最后再把这个Properties对象作为属性保存到ServletContext中。 LoadServlet类的init方法
public void init()throws ServletException {
Properties ps=new Properties();
try{
ServletContext context=getServletContext();
InputStream in=
context.getResourceAsStream("/WEB-INF/messageresource.properties");
ps.load(in);
in.close();
context.setAttribute("ps",ps);
}catch(Exception e){
e.printStackTrace();
}
}
为了保证在Web应用启动时就加载LoadServlet,应该在web.xml中配置这个Servlet时设置load-on-startup属性:
<servlet>
<servlet-name>load</servlet-name>
<servlet-class>mypack.LoadServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
创建MessageTag标签处理类 MessageTag包含一个成员变量key,它与message标签的属性key对应。在MessageTag中定义了getKey和setKey方法:
private String key=null;
public String getKey(){
return this.key;
}
public void setKey(String key){
this.key=key;
}
public int doEndTag() throws JspException {
try {
Properties ps=
(Properties)pageContext.getAttribute("ps",pageContext.APPLICATION_SCOPE);
String message=(String)ps.get(key);
pageContext.getOut().print(message);
} catch (Exception e) {
throw new JspTagException(e.getMessage());
}
return EVAL_PAGE;
}
}
在mytaglib库中定义message标签
<tag>//定义一个标签
<name>message</name>//标签名
<tag-class>mypack.MessageTag</tag-class>//标签处理类
<body-content>empty</body-content>//标签主体的类型
<info>produce message by key</info>//标签的说明信息
<attribute>
<name>key</name>//属性
<required>true</required>//属性是否需要赋值
</attribute>
</tag>
5. 练习题
- 问题:在标签处理类中,如何访问session范围内的共享数据?
选项: (A)在TagSupport类中定义了session成员变量,直接调用它的getAttribute()方法即可。 (B)在标签处理类TagSupport类中定义了pageContext成员变量,先通过它的getSession()方法获得当前的HttpSession对象,再调用HttpSession对象的getAttribute()方法。 ?pageContext.getAttribute(“attributename”,PageContext.SESSION_SCOPE) 答案: B,C - 问题:在下面的选项中,哪些是TagSupport类的doStartTag()方法的有效返回值?
选项: (A) Tag.SKIP_BODY (B) Tag.SKIY_PAGE ? Tag.EVAL_BODY_INCLUDE (D) Tag.EVAL_PAGE 答案: A,C - 问题:标签处理类的doStartTag()方法以及doEndTag()方法由谁调用?
选项: (A) HTTPServletRequest类 (B) HTTPServletResponse类 ? ServletContext类 (D) Servlet容器 答案: D
|