一、JSP自定义标签
我们知道,JSP在被访问时会被JSP引擎翻译为Servlet程序,即JSP就是Servlet程序。我们可以在JSP中插入Java代码,但是在JSP页面中使用<%...%>嵌入JAVA代码,使用页面编写起来十分混乱,更不利于以后的维护。因为,JSP自定义标签可以优雅的解决这一问题。JSTL也正是SUN为些开发的一个标签库,接下来让我们来编写自己的自定义标签。
定义JSP自定义标签,需要四个步骤:
1. 编写一个实现Tag接口的Java类(标签处理器类),覆盖其中的doStartTag方法,在doStartTag方法中编写标签的功能代码。
2. 编写标签库描述符(tld)文件,在tld文件中对自定义标签进行描述。
3. 在WEB应用中部署和安装自定义标签库。
4. 在JSP页面中导入和使用自定义标签。
在以前的JSP页面编写中,我们有使用过“<cc:if test=”逻辑表达方式”>”和<cc:froEach var=”变量” items=”集合、列表、数组”>。下面我们就来实现与这两个JSP标签类似功能的自定义JSP标签。
1.编写<cc:if test=”…”>…</cc:forEach>自定义标签:
IfTag.java,标签处理器:
import javax.servlet.jsp.*;
import javax.servlet.jsp.tagext.Tag;
public class IfTag implements Tag {
private boolean test;
public void setTest(boolean test) {
this.test = test;
}
public int doStartTag() throws JspException {
if(this.test)
return Tag.EVAL_BODY_INCLUDE;
return Tag.SKIP_BODY;
}
public int doEndTag() throws JspException {
// TODO Auto-generated method stub
return 0;
}
public Tag getParent() {
// TODO Auto-generated method stub
return null;
}
public void release() {
// TODO Auto-generated method stub
}
public void setPageContext(PageContext arg0) {
// TODO Auto-generated method stub
}
public void setParent(Tag arg0) {
// TODO Auto-generated method stub
}
}
|
1. “private boolean test;”,这个成员名称必须与JSP中自定义标签的属性名称相同,JSP引擎会通过“setTest”方法,将属性值传递过来。
2. “doStartTag()”当自定义标签开始被执行时,JSP引擎会调此方法。在此方法中可以“Tag.EVAL_BODY_INCLUDE”告诉JSP引擎继续向下执行标签体中的内容,如果返回“Tag.SKIP_BODY”JSP引擎将不执行标签体中的内容。
3. “doEndTag()”当标签体被执行完成后,会调用些方法。
4. “getParent()”返回父标签。
5. “release()”,当标签处理器被销毁前会调用此方法,可以在此方法中释放。但这个处理器被JSP引擎实例化后,一般不会被释放而是保存在内存中,留以后用。服务器被关闭时,会被释放。
6. “setPageContext(PageContext arg0)”,JSP引擎将JSP页面的运行环境,通过此方法传递给自定义标签处理器。
7. “setParent(Tag arg0)”,如果标签有父标签时,JSP引擎会将父标签对象传递进来。
MyEl.tld,自定义标签描述文件。
<?xml version="1.0" encoding="UTF-8"?>
<taglib version="2.0" xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd">
<tlib-version>1.0</tlib-version>
<short-name>SimpleTagLibrary</short-name>
<uri>/SimpleTagLibrary</uri>
<tag>
<name>myIf</name>
<tag-class>cn.itcast.cc.jsptag.IfTag</tag-class>
<body-content>JSP</body-content>
<attribute>
<name>test</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
</taglib>
|
1. tld文件在工程中的存放位置,与web.xml存位置相同。
2. “<uri>”设置tld描述文件的URI,URI用于在JSP页面中引入此tld文件。
3. “<tag>”定义一个自定义标签。
4. “<name>”自定义标签名。
5. “<tag-class>”自定义标签处理器类,就是上边编写的类。
6. “<body-content>”自定义标签的标签体内容。(也可以设置为“empty”等)
7. “<attribute>”为自定义标签添加一个属性。
8. “<name>”自定义标签属性名。
9. “<required>”true为必须指定此属性,false此属性可为空。
10. “<rtexprvalue>”设置属性为静态的或是动态的。如果为false即静态的,静态的属性值JSP引擎会将它自动转换(必须是java的8种基本数据类型),比如<cc:MyIf test=”122”>JSP引擎会自动将“123”转换为整数型,所以处理器的类成员可以定义为int型。如果为true即动态的,如果设置为将类型设置为Object可以接收任意数据类型的属性。
Index.jsp,在JSP页面中引入自定义标签,并调用自定义标签:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@taglib prefix="cc" uri="/SimpleTagLibrary" %>
<html>
<head>
</head>
<body>
<cc:myIf test="${2>1}">
自定义逻辑判断标签!
</cc:myIf>
</body>
</html>
|
2.编写<cc:foEach var=”temp” items=”list”>…</cc:forEach>自定义标签:
forEahTag.java,继承自SimpleTagSupport,这个简单简化了Tag接口的操作。
import java.io.IOException;
import java.util.*;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.SimpleTagSupport;
public class forEahTag extends SimpleTagSupport {
// 记录 items="xxx" 中的list对象
private List items;
// 记录 var="xxx"中的变量名称
private String var;
public void setItems(List items) {
this.items = items;
}
public void setVar(String var) {
this.var = var;
}
// SimpleTagSupport简化了Tag接口的操作,在此只需要覆盖doTag方法即可实现forEach。
@Override
public void doTag() throws JspException, IOException {
Iterator iter = this.items.iterator();
while (iter.hasNext()) {
// 将单个对象设置到Context域中,在JSP页面中可以使用${var}获取对象。
this.getJspContext()
.setAttribute(this.var, iter.next());
// 调用执行标签体,需要一个输出流参数,
// 设置为null时JSP引擎自动将其替换为this.getJspContext().getOut()
this.getJspBody().invoke(null);
}
}
}
|
“this.getJspBody()”返回JspFragment对象,JspFragment代表一个标签体。JspFragment中不能包含JSP脚本元素。JspFragment.invoke就是调用标签体。如果不调用此方法,就是忽略标签体。
forEach也可以使用实现TagSupport接口实现,当标签体被调用时,首先会调用“doStartTag”方法(只会调用一次),可以在这个方法里先输出第一个成员。然后执行标签体,最后调用“doAfterBody”方法,它是重点。在这个方法里再输出下一个成员然后判断是不是到了list尾,如果到了list结尾则返回IterationTag. SKIP_BODY;。如果没有到list结尾则返回IterationTag. EVAL_BODY_AGAIN;,继续重复执行标签体,实现对list的遍历!
MyEl.tld,添加如下内容:
<tag>
<name>myForEach</name>
<tag-class>cn.itcast.cc.jsptag.forEahTag</tag-class>
<body-content>scriptless</body-content>
<attribute>
<name>var</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>items</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
|
“<body-content>scriptless</body-content>”指定标签体内为非脚本代码。
Index.jsp添加如下内容:
<%
List list = new ArrayList();
list.add("一");
list.add("二");
list.add("三");
this.getServletContext().setAttribute("list",list);
%>
<cc:myForEach items="${list}" var="temp">
${temp }
</cc:myForEach>
|
在JSP页面插入的代码,是向List对象中插入成员,然后添加到ServletContext域中,${list}在ServletContext域中获取list对象。
3.JSP自定义标签高级部分:
修改标签内容的标签<cc:htmlFilter>与<cc:readFile>两个标签配合使用。
ReadFileTag.java
public class ReadFileTag extends SimpleTagSupport {
// 记录文件路径
private String src;
public void setSrc(String src) {
this.src = src;
}
@Override
public void doTag() throws JspException, IOException {
// 获取到文件的全路径
PageContext pc = (PageContext) this.getJspContext();
ServletContext sc = pc.getServletContext();
String file = sc.getRealPath(src);
// 使用字符缓冲流读取文件
BufferedReader reader = new BufferedReader(newFileReader(file));
String line = null;
while((line = reader.readLine()) != null){
// 写到输出流
this.getJspContext().getOut().write(line + "\r\n");
}
}
}
|
注意代码中的写到输出流,这个输出流是父标签传递进来的输出流。
HtmlFilter.java
public class HtmlFilter extends SimpleTagSupport {
@Override
public void doTag() throws JspException, IOException {
// 调用子标签,并将输出流传递进去。
CharArrayWriter caw = new CharArrayWriter();
this.getJspBody().invoke(caw);
//这里之所以要写义一个输出流,是为了实现Html字符的转换
this.getJspContext().getOut().write(filter(caw.toString()));
System.out.println(caw.toString());
}
// Html字符转换
public static String filter(String message) {
if (message == null)
return (null);
char content[] = new char[message.length()];
message.getChars(0, message.length(), content, 0);
StringBuffer result = new StringBuffer(content.length + 50);
for (int i = 0; i < content.length; i++) {
switch (content[i]) {
case '<':
result.append("<");
break;
case '>':
result.append(">");
break;
case '&':
result.append("&");
break;
case '"':
result.append(""");
break;
default:
result.append(content[i]);
}
}
return (result.toString());
}
}
|
调用标签体时,之所以传递一个自定义的输出流。是因为,使用这个输出流,获取读到的html文件的内容。然后将流中的html字符转换后,再输出到JSP页面。
二、JSTL标签
1.jstl标签分为:
核心标签库
国际化标签
数据库标签
XML标签
JSTL函数
因为在JSP页面中最好不要访问数据库和XML文件,这样太不安全了而且程序逻辑混乱,所以就没有讲解这方面的使用。
2.jstl常用的标签:
(1).<c:out>标签:用于输出一段文本内容到pageContext对象当前保存的“out”对象中。
(2). <c:set>标签:用于设置各种Web域中的属性,或者设置Web域中的java.util.Map类型的属性对象或JavaBean类型的属性对象的属性。
(3). <c:remove>标签:用于删除各种Web域中的属性。
(4). <c:catch>标签:用于捕获嵌套在标签体中的内容抛出的异常。
(5). <c:if test=“”>标签:可以构造简单的“if-then”结构的条件表达式。
(6). <c:choose>标签:用于指定多个条件选择的组合边界,它必须与<c:when>和<c:otherwise>标签一起使用。
(7). <c:forEach>标签:用于对一个集合对象中的元素进行循环迭代操作,或者按指定的次数重复迭代执行标签体中的内容。
(8). <c:param>标签:它可以嵌套在<c:import>、<c:url>或<c:redirect>标签内,为这些标签所使用的URL地址附加参数。此标签自动进行URL编码。
(9). <c:url>标签:用于在JSP页面中构造一个URL地址,其主要目的是实现URL重写。还记得URL重写是什么吗?当session被禁用时,URL重写就是为解决这一问题的!
(10). <c:redirect>标签:用于将当前的访问请求转发或重定向到其他资源。
OK,上面是常用的标签,使用方法去查手册吧!东西太多了。再来一个标签,Struts中使用较多——C:check permission。
比如,一个管理页面,有好多管理选项。但管理选项需要根据用户的权限进行显示,此时自定义一个检查标签,将User做为参数传递给标签处理器。标签处理器查看User的权限,然后确定是否执行标签体。(标签体中的内容为显示相应的管理选项)