除了JavaMail今日是JavaWeb部分的最后一天了,还有三天的时间是做个小项目。将之前学习的所有知识串起来,准备迎接高级部分。嘿嘿!终于到了高级部分!
今日主要内容是JSP自定义标签与JSTL,之前有使用过JSTL,十分的方便,还有EL。也有学过自定义EL函数,但个人感觉EL自定义函数没有JSP的自定义标签有趣。
一、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(new FileReader(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,上面是常用的标签,使用方法去查手册吧!东西太多了。老方今天特别提到一个标签,是佟老师特别指出的。它在Structs中使用较多——C:check permission。
比如,一个管理页面,有好多管理选项。但管理选项需要根据用户的权限进行显示,此时自定义一个检查标签,将User做为参数传递给标签处理器。标签处理器查看User的权限,然后确定是否执行标签体。(标签体中的内容为显示相应的管理选项)
OK,明天就要老方就开始编写一个将JavaSeb基础部分的所有内容融会到一起的WEB应用用例。估计使用1天多点的时间。然后同学们有近两天的个人实现时间。大家对此都十分期待!终于应用这段时间所学的知识,写个像样的东西出来。
加油!