已经好久没有写博客了,这段时间看过很多书,包括《浪潮之巅》(好书,推荐大家看看),《暗时间》(也很不错,刘末鹏讲时间管理的),还有断断续续研究了IOC和spring的实现代码,这部分收获不大,单纯的为了看代码而看代码,现在打算学习设计模式后再来深入。这段时间,在看martin的《企业应用模式》,这是本好书,但翻译看着感觉怪怪的,好多名词怪怪的。但买了就要学习一下,基本看下还是可以的。
今天来重新学习一下基本的东西。很久以前就学习过JSP自定义标签,JSTL更是经常使用,但说到写一个自定义标签,如果没有重新看一下,倒真不知道如何下手。相信很多朋友有同样的感觉,看过的东西,没用一段时间很快就忘记了,特别是技术方面的。究其原因,还是我们没能深入了解它的本质,没有深入理解。
我们一起来看一下。
首先我们必须知道一个标签可以包含什么东西:拿一个JSTL核心标签来看一下:
<c:forEach var="item" items="${items}"> XXXX </c:forEach>
我们看到,一个标签可以包含属性,这个可以随便,自己定个数,也可以有标签体,当然这个标签体又可以是另外一个标签,如此类推。
那究竟怎么来实现呢?首先我们可以看看JAVA EE中的Tag相应的层级结构。
我们可以看到基本的接口是JspTag,当然我们没必要去实现这个接口,因为JAVA EE已经帮我们实现了一部分,如果
我们不需要标签体,我们可以简单地继续TagSupport,而如果我们需要标签体,我们可以直接继承BodyTagSupport。这
个在我们之后的例子可以看到。(我们只看有标签体的例子,没有标签体的大家可以自己试下,很简单)。
那么基本的信息,我们知道了,是时候开始代码了。建项目这些就不废话了。
自定义标签包括如下几个步骤:
1)自定义标签处理类(即我们刚才说的继承TagSupport或BodyTagSupport,或者在新的JSP2里面的SimpleTag)。
2)一个标签描述文件,tld,这个我们可以通过打JAR包或者直接放在META—INF文件夹下,容器会自动进行搜索。
3)在web.xml中进行配置jsp-config进行配置taglib-uri及taglig-location。
4)在JSP页面上用taglib命令引用,然后就可以使用了。
看起来很简单,对吧。实际上也就那么回事。开始我们的代码。
1)首先来自定义标签处理类:
我这里写了一个进行循环输出的,这里我暂时处理 的是String类型,其他类型的大致相同。
package com.shun.customtag; import java.io.IOException; import java.util.Collection; import java.util.Iterator; import javax.servlet.jsp.JspException; import javax.servlet.jsp.tagext.BodyTagSupport; public class IteratorTag extends BodyTagSupport{ private static final long serialVersionUID = 1L; private Iterator<String> it; private String name;//这里name作为循环的变量名称,相当于forEach中的var public void setName(String name) { this.name = name; } public void setItems(Collection<String> items){ if(items.size() > 0) { it = items.iterator(); } } public int doAfterBody() throws JspException { return iterateItems(); } public int doEndTag() throws JspException { //这里必须把body里面的内容输出,否则标签内的内容会为空 if (bodyContent != null) { try { bodyContent.writeOut(bodyContent.getEnclosingWriter()); } catch (IOException e) { e.printStackTrace(); } } return EVAL_PAGE; } public int doStartTag() throws JspException { if (it == null) { return SKIP_BODY; } else { return iterateItems(); } } /** * 这里进行遍历传入的items对象 * @return */ private int iterateItems(){ //如果还存在值,则把它放入pageContext,即当前页面下,以便我们可以在body中进行取出 if (it.hasNext()) { pageContext.setAttribute(name, it.next()); return EVAL_BODY_AGAIN; } return EVAL_PAGE; } }
看到这里,也许有些人有疑问,怎么来确定返回的是EVAL_啥呢?
这个基本上是下面的情况:
doStartTag(标签处理开始,即遇到开始标签时)一般返回EVAL_BODY_INCLUDE或者SKIP_BODY
doEndTag(标签处理结束,即遇到结束标签时)一般返回EVAL_PAGE或者SKIP_PAGE。
doAfterBody(标签体处理完成后)一般可以返回EVAL_BODY_AGAIN或者EVAL_BODY_BUFFER(这个是JSP2新的,JSTL1.1是没有的)或者EVAL_PAGE
应该很容易想明白,在开始标签处理完后,就要考虑标签体的处理,那么EVAL_BODY_INCLUDE或者SKIP_BODY就很自然了,在结束后就开始考虑剩余的页面处理,那么返回SKIP_PAGE或者EVAL_PAGE也很有道理,而doAfterBody就是在标签体处理完成后要干什么,可以继续处理页面EVAL_PAGE,或者重新处理标签体(这个以前用的是EVAL_BODY_TAG,但这个已经在JAVA EE6中被抛弃了)。
代码基本上没啥问题的。可能很多人会说,那个bodyContent是干啥用的。这个实际上可以把它看成一个内容的缓冲器,它把我们所有标签体中的内容进行缓冲,当我们调用writeOut时,才进行写出。
2)标签处理类有了,那么接着就来标签描述文件了。
<?xml version="1.0" encoding="UTF-8" ?> <taglib 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 web-jsptaglibrary_2_0.xsd" version="2.0"> <description>A tag library exercising SimpleTag handlers.</description> <tlib-version>1.0</tlib-version> <short-name>myTag</short-name> <uri>http://com.shun/myTag</uri> <tag> <name>iterator</name> <tag-class>com.shun.customtag.IteratorTag</tag-class> <body-content>JSP</body-content> <attribute> <name>items</name> <required>true</required> <rtexprvalue>true</rtexprvalue> </attribute> <attribute> <name>name</name> <required>true</required> <rtexprvalue>true</rtexprvalue> </attribute> </tag> </taglib>
这里也是简单得很,但有几个地方要注意:
(1)这里的URI和我们稍后配置的在web.xml中的taglib-uri需要一致,容器就是靠这个URI来查找标签库的。
(2)rtexprvalue为true表明了指定的属性可以用EL表达式或其他的JSTL标签等。
3)写完之后就来配置web.xml了:
简单的一段配置就OK了。
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0"> <jsp-config> <taglib> <taglib-uri>http://com.shun/myTag</taglib-uri> <taglib-location>/META-INF/myTag.tld</taglib-location> </taglib> </jsp-config> </web-app>
这里taglib-uri在上面已经强调过,需要和引用的tld中的URI一致。
4)接下来我们就可以直接使用了:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ page import="java.util.*" %> <%@ taglib uri="http://com.shun/myTag" prefix="myTag" %> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Insert title here</title> </head> <body> <% List<String> items = new ArrayList<String>(); items.add("test1"); items.add("test2"); items.add("test3"); request.setAttribute("items",items); %> <myTag:iterator items="${items }" name="item"> ${item } </myTag:iterator> </body> </html>
这里我们构造了一个List,然后让它循环输出,很简单。
当我们进行访问时,可以看到:
这样表明我们的标签运行正常,这样就搞定了我们的自定义标签开发了。当然,当我们项目需要用到时,肯定不会这么简单的,这里只是抛砖引玉,大家可以深入研究。