JavaWeb开发与代码的编写(十)
自定义标签
自定义标签主要用于移除Jsp页面中的java代码。
编写一个实现Tag接口的Java类(标签处理器类)
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.Tag;
public class ViewIPTag implements Tag {
//接收传递进来的PageContext对象
private PageContext pageContext;
@Override
public int doEndTag() throws JspException {
System.out.println("调用doEndTag()方法");
return 0;
}
@Override
public int doStartTag() throws JspException {
System.out.println("调用doStartTag()方法");
HttpServletRequest request =(HttpServletRequest) pageContext.getRequest();
JspWriter out = pageContext.getOut();
String ip = request.getRemoteAddr();
try {
//这里输出的时候会抛出IOException异常
out.write(ip);
} catch (IOException e) {
//捕获IOException异常后继续抛出
throw new RuntimeException(e);
}
return 0;
}
@Override
public Tag getParent() {
return null;
}
@Override
public void release() {
System.out.println("调用release()方法");
}
@Override
public void setPageContext(PageContext pageContext) {
System.out.println("setPageContext(PageContext pageContext)");
this.pageContext = pageContext;
}
@Override
public void setParent(Tag arg0) {
}
}
2、在WEB-INF/目录下新建tld文件,在tld文件中对标签处理器类进行描述
gacl.tld文件的代码如下:
孤傲苍狼开发的自定义标签库
1.0
GaclTagLibrary
/gacl
这个标签的作用是用来输出客户端的IP地址
viewIP
me.gacl.web.tag.ViewIPTag
empty
在Jsp页面中使用自定义标签
1、使用""指令引入要使用的标签库。
例如:在jspTag_Test1.jsp中引用gacl标签库
输出客户端的IP
你的IP地址是(使用java代码获取输出):
你的IP地址是(使用自定义标签获取输出):
标签的运行效果如下:
从运行效果种可以看到,使用自定义标签就可以将jsp页面上的java代码移除掉,如需要在jsp页面上输出客户端的IP地址时,使用 标签就可以代替jsp页面上的这些代码:
这就是开发和使用自定义标签的好处,可以让我们的Jsp页面上不嵌套java代码。
自定义标签的执行流程
JSP引擎遇到自定义标签时,首先创建标签处理器类的实例对象,然后按照JSP规范定义的通信规则依次调用它的方法。 1、public void setPageContext(PageContext pc), JSP引擎实例化标签处理器后,将调用setPageContext方法将JSP页面的pageContext对象传递给标签处理器,标签处理器以后可以通过这个pageContext对象与JSP页面进行通信。 2、public void setParent(Tag t),setPageContext方法执行完后,WEB容器接着调用的setParent方法将当前标签的父标签传递给当前标签处理器,如果当前标签没有父标签,则传递给setParent方法的参数值为null。 3、public int doStartTag(),调用了setPageContext方法和setParent方法之后,WEB容器执行到自定义标签的开始标记时,就会调用标签处理器的doStartTag方法。 4、public int doEndTag(),WEB容器执行完自定义标签的标签体后,就会接着去执行自定义标签的结束标记,此时,WEB容器会去调用标签处理器的doEndTag方法。 5、public void release(),通常WEB容器执行完自定义标签后,标签处理器会驻留在内存中,为其它请求服务器,直至停止web应用时,web容器才会调用release方法。
我们在tomcat服务器的"work\Catalina\localhost\JavaWeb_JspTag_study_20140816\org\apache\jsp"目录下可以找到将jspTag_Test1.jsp翻译成Servlet后的java源代码,如下图所示:
打开jspTag_005fTest1_jsp.java文件,可以看到setPageContext(PageContext pc)、setParent(Tag t)、doStartTag()、doEndTag()、release()这5个方法的调用顺序和过程。
jspTag_005fTest1_jsp.java的代码如下:
3 import javax.servlet.*;
4 import javax.servlet.http.*;
5 import javax.servlet.jsp.*;
6
7 public final class jspTag_005fTest1_jsp extends org.apache.jasper.runtime.HttpJspBase
8 implements org.apache.jasper.runtime.JspSourceDependent {
9
10 private static final JspFactory _jspxFactory = JspFactory.getDefaultFactory();
11
12 private static java.util.List _jspx_dependants;
13
14 static {
15 _jspx_dependants = new java.util.ArrayList(1);
16 _jspx_dependants.add("/WEB-INF/gacl.tld");
17 }
18
19 private org.apache.jasper.runtime.TagHandlerPool _005fjspx_005ftagPool_005fxdp_005fviewIP_005fnobody;
20
21 private javax.el.ExpressionFactory _el_expressionfactory;
22 private org.apache.AnnotationProcessor _jsp_annotationprocessor;
23
24 public Object getDependants() {
25 return _jspx_dependants;
26 }
27
28 public void _jspInit() {
29 _005fjspx_005ftagPool_005fxdp_005fviewIP_005fnobody = org.apache.jasper.runtime.TagHandlerPool.getTagHandlerPool(getServletConfig());
30 _el_expressionfactory = _jspxFactory.getJspApplicationContext(getServletConfig().getServletContext()).getExpressionFactory();
31 _jsp_annotationprocessor = (org.apache.AnnotationProcessor) getServletConfig().getServletContext().getAttribute(org.apache.AnnotationProcessor.class.getName());
32 }
33
34 public void _jspDestroy() {
35 _005fjspx_005ftagPool_005fxdp_005fviewIP_005fnobody.release();
36 }
37
38 public void _jspService(HttpServletRequest request, HttpServletResponse response)
39 throws java.io.IOException, ServletException {
40
41 PageContext pageContext = null;
42 HttpSession session = null;
43 ServletContext application = null;
44 ServletConfig config = null;
45 JspWriter out = null;
46 Object page = this;
47 JspWriter _jspx_out = null;
48 PageContext _jspx_page_context = null;
49
50
51 try {
52 response.setContentType("text/html;charset=UTF-8");
53 pageContext = _jspxFactory.getPageContext(this, request, response,
54 null, true, 8192, true);
55 _jspx_page_context = pageContext;
56 application = pageContext.getServletContext();
57 config = pageContext.getServletConfig();
58 session = pageContext.getSession();
59 out = pageContext.getOut();
60 _jspx_out = out;
61
62 out.write("\r\n");
63 out.write("\r\n");
64 out.write("\r\n");
65 out.write("\r\n");
66 out.write("\r\n");
67 out.write(" \r\n");
68 out.write(" 输出客户端的IP\r\n");
69 out.write(" \r\n");
70 out.write(" \r\n");
71 out.write(" \r\n");
72 out.write(" 你的IP地址是(使用java代码获取输出):\r\n");
73 out.write(" ");
74
75 //在jsp页面中使用java代码获取客户端IP地址
76 String ip = request.getRemoteAddr();
77 out.write(ip);
78
79 out.write("\r\n");
80 out.write(" \r\n");
81 out.write(" 你的IP地址是(使用自定义标签获取输出):");
82 if (_jspx_meth_xdp_005fviewIP_005f0(_jspx_page_context))
83 return;
84 out.write("\r\n");
85 out.write(" \r\n");
86 out.write("\r\n");
87 } catch (Throwable t) {
88 if (!(t instanceof SkipPageException)){
89 out = _jspx_out;
90 if (out != null && out.getBufferSize() != 0)
91 try { out.clearBuffer(); } catch (java.io.IOException e) {}
92 if (_jspx_page_context != null) _jspx_page_context.handlePageException(t);
93 }
94 } finally {
95 _jspxFactory.releasePageContext(_jspx_page_context);
96 }
97 }
98
99 private boolean _jspx_meth_xdp_005fviewIP_005f0(PageContext _jspx_page_context)
100 throws Throwable {
101 PageContext pageContext = _jspx_page_context;
102 JspWriter out = _jspx_page_context.getOut();
103 // xdp:viewIP
104 me.gacl.web.tag.ViewIPTag _jspx_th_xdp_005fviewIP_005f0 = (me.gacl.web.tag.ViewIPTag) _005fjspx_005ftagPool_005fxdp_005fviewIP_005fnobody.get(me.gacl.web.tag.ViewIPTag.class);
105 _jspx_th_xdp_005fviewIP_005f0.setPageContext(_jspx_page_context);
106 _jspx_th_xdp_005fviewIP_005f0.setParent(null);
107 int _jspx_eval_xdp_005fviewIP_005f0 = _jspx_th_xdp_005fviewIP_005f0.doStartTag();
108 if (_jspx_th_xdp_005fviewIP_005f0.doEndTag() == javax.servlet.jsp.tagext.Tag.SKIP_PAGE) {
109 _005fjspx_005ftagPool_005fxdp_005fviewIP_005fnobody.reuse(_jspx_th_xdp_005fviewIP_005f0);
110 return true;
111 }
112 _005fjspx_005ftagPool_005fxdp_005fviewIP_005fnobody.reuse(_jspx_th_xdp_005fviewIP_005f0);
113 return false;
114 }
115 }
下面重点分析一下上述代码中标红色的那个 private boolean _jspx_meth_xdp_005fviewIP_005f0(PageContext _jspx_page_context)方法中的代码
①、这里是实例化一个viewIP标签处理器类me.gacl.web.tag.ViewIPTag的对象
// xdp:viewIP
me.gacl.web.tag.ViewIPTag _jspx_th_xdp_005fviewIP_005f0 = (me.gacl.web.tag.ViewIPTag) _005fjspx_005ftagPool_005fxdp_005fviewIP_005fnobody.get(me.gacl.web.tag.ViewIPTag.class);
②、实例化标签处理器后,调用setPageContext方法将JSP页面的pageContext对象传递给标签处理器
_jspx_th_xdp_005fviewIP_005f0.setPageContext(_jspx_page_context);
③、setPageContext方法执行完后,接着调用的setParent方法将当前标签的父标签传递给当前标签处理器,如果当前标签没有父标签,则传递给setParent方法的参数值为null
_jspx_th_xdp_005fviewIP_005f0.setParent(null);
④、调用了setPageContext方法和setParent方法之后,WEB容器执行到自定义标签的开始标记时,就会调用标签处理器的doStartTag方法
int _jspx_eval_xdp_005fviewIP_005f0 = _jspx_th_xdp_005fviewIP_005f0.doStartTag();
⑤、WEB容器执行完自定义标签的标签体后,就会接着去执行自定义标签的结束标记,此时,WEB容器会去调用标签处理器的doEndTag方法
if (_jspx_th_xdp_005fviewIP_005f0.doEndTag() == javax.servlet.jsp.tagext.Tag.SKIP_PAGE)
这就是自定义标签的执行流程。
传统标签
标签技术的API类继承关系
在现在的jsp标签开发中,很少直接使用传统标签来开发了,目前用得较多的都是简单标签,所以Jsp的传统标签开发了解一下即可。
标签API介绍
JspTag接口
JspTag接口是所有自定义标签的父接口,它是JSP2.0中新定义的一个标记接口,没有任何属性和方法。JspTag接口有Tag和SimpleTag两个直接子接口,JSP2.0以前的版本中只有Tag接口,所以把实现Tag接口的自定义标签也叫做传统标签,把实现SimpleTag接口的自定义标签叫做简单标签。
Tag接口
Tag接口是所有传统标签的父接口,其中定义了两个重要方法(doStartTag、doEndTag)方法和四个常量(EVAL_BODY_INCLUDE、SKIP_BODY、EVAL_PAGE、SKIP_PAGE),这两个方法和四个常量的作用如下:
(1)WEB容器在解释执行JSP页面的过程中,遇到自定义标签的开始标记就会去调用标签处理器的doStartTag方法,doStartTag方法执行完后可以向WEB容器返回常量EVAL_BODY_INCLUDE或SKIP_BODY。如果doStartTag方法返回EVAL_BODY_INCLUDE,WEB容器就会接着执行自定义标签的标签体;如果doStartTag方法返回SKIP_BODY,WEB容器就会忽略自定义标签的标签体,直接解释执行自定义标签的结束标记。
(2)WEB容器解释执行到自定义标签的结束标记时,就会调用标签处理器的doEndTag方法,doEndTag方法执行完后可以向WEB容器返回常量EVAL_PAGE或SKIP_PAGE。如果doEndTag方法返回常量EVAL_PAGE,WEB容器就会接着执行JSP页面中位于结束标记后面的JSP代码;如果doEndTag方法返回SKIP_PAGE,WEB容器就会忽略JSP页面中位于结束标记后面的所有内容。
从doStartTag和doEndTag方法的作用和返回值的作用可以看出,开发自定义标签时可以在doStartTag方法和doEndTag方法体内编写合适的Java程序代码来实现具体的功能,通过控制doStartTag方法和doEndTag方法的返回值,还可以告诉WEB容器是否执行自定义标签中的标签体内容和JSP页面中位于自定义标签的结束标记后面的内容。
IterationTag接口
IterationTag接口继承了Tag接口,并在Tag接口的基础上增加了一个doAfterBody方法和一个EVAL_BODY_AGAIN常量。实现IterationTag接口的标签除了可以完成Tag接口所能完成的功能外,还能够通知WEB容器是否重复执行标签体内容。对于实现了IterationTag接口的自定义标签,WEB容器在执行完自定义标签的标签体后,将调用标签处理器的doAfterBody方法,doAfterBody方法可以向WEB容器返回常量EVAL_BODY_AGAIN或SKIP_BODY。如果doAfterBody方法返回EVAL_BODY_AGAIN,WEB容器就会把标签体内容再重复执行一次,执行完后接着再调用doAfterBody方法,如此往复,直到doAfterBody方法返回常量SKIP_BODY,WEB容器才会开始处理标签的结束标记和调用doEndTag方法。
可见,开发自定义标签时,可以通过控制doAfterBody方法的返回值来告诉WEB容器是否重复执行标签体内容,从而达到循环处理标签体内容的效果。例如,可以通过一个实现IterationTag接口的标签来迭代输出一个集合中的所有元素,在标签体部分指定元素的输出格式。
在JSP API中也提供了IterationTag接口的默认实现类TagSupport,我们在编写自定义标签的标签处理器类时,可以继承和扩展TagSupport类,这相比实现IterationTag接口将简化开发工作。
BodyTag接口
BodyTag接口继承了IterationTag接口,并在IterationTag接口的基础上增加了两个方法(setBodyContent、doInitBody)和一个EVAL_BODY_BUFFERED常量。实现BodyTag接口的标签除了可以完成IterationTag接口所能完成的功能,还可以对标签体内容进行修改。对于实现了BodyTag接口的自定义标签,标签处理器的doStartTag方法不仅可以返回前面讲解的常量EVAL_BODY_INCLUDE或SKIP_BODY,还可以返回常量EVAL_BODY_BUFFERED。如果doStartTag方法返回EVAL_BODY_BUFFERED,WEB容器就会创建一个专用于捕获标签体运行结果的BodyContent对象,然后调用标签处理器的setBodyContent方法将BodyContent对象的引用传递给标签处理器,WEB容器接着将标签体的执行结果写入到BodyContent对象中。在标签处理器的后续事件方法中,可以通过先前保存的BodyContent对象的引用来获取标签体的执行结果,然后调用BodyContent对象特有的方法对BodyContent对象中的内容(即标签体的执行结果)进行修改和控制其输出。
在JSP API中也提供了BodyTag接口的实现类BodyTagSupport,我们在编写能够修改标签体内容的自定义标签的标签处理器类时,可以继承和扩展BodyTagSupport类,这相比实现BodyTag接口将简化开发工作。
SimpleTag接口
SimpleTag接口是JSP2.0中新增的一个标签接口。由于传统标签使用三个标签接口来完成不同的功能,显得过于繁琐,不利于标签技术的推广,因此,SUN公司为降低标签技术的学习难度,在JSP 2.0中定义了一个更为简单、便于编写和调用的SimpleTag接口。SimpleTag接口与传统标签接口最大的区别在于,SimpleTag接口只定义了一个用于处理标签逻辑的doTag方法,该方法在WEB容器执行自定义标签时调用,并且只被调用一次。那些使用传统标签接口所完成的功能,例如是否执行标签体、迭代标签体、对标签体内容进行修改等功能都可以在doTag方法中完成。
在JSP API中也提供了SimpleTag接口的实现类SimpleTagSupport,我们在编写简单标签时,可以继承和扩展SimpleTagSupport类,这相比实现SimpleTag接口将简化开发工作。
传统标签接口中的各个方法可以返回的返回值说明
下图列举了Tag接口、IterationTag接口和BodyTag接口中的主要方法及它们分别可以返回的返回值的说明。
开发传统标签实现页面逻辑
开发人员在编写Jsp页面时,经常还需要在页面中引入一些逻辑,例如:
- 控制jsp页面某一部分内容是否执行。
- 控制整个jsp页面是否执行。
- 控制jsp页面内容重复执行。
- 修改jsp页面内容输出。
自定义标签除了可以移除jsp页面java代码外,它也可以实现以上功能。
控制jsp页面某一部分内容是否执行
编写一个类实现tag接口,控制doStartTag()方法的返回值,如果这个方法返回EVAL_BODY_INCLUDE,则执行标签体,如果返回SKIP_BODY,则不执行标签体。
SUN公司针对tag接口提供了一个默认的实现类TagSupport,TagSupport类中实现了tag接口的所有方法,因此我们可以编写一个类继承TagSupport类,然后再重写doStartTag方法。
示例代码如下:
TagDemo1.java
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.Tag;
import javax.servlet.jsp.tagext.TagSupport;
/**
* @author gacl
* TagSupport类实现了Tag接口,TagDemo1继承TagSupport类
*
*/
public class TagDemo1 extends TagSupport {
/* 重写doStartTag方法,控制标签体是否执行
* @see javax.servlet.jsp.tagext.TagSupport#doStartTag()
*/
@Override
public int doStartTag() throws JspException {
//如果这个方法返回EVAL_BODY_INCLUDE,则执行标签体,如果返回SKIP_BODY,则不执行标签体
//return Tag.EVAL_BODY_INCLUDE;
return Tag.SKIP_BODY;
}
}
在WEB-INF目录下的tld文件中添加对该标签处理类的描述,如下:
demo1
me.gacl.web.tag.TagDemo1
JSP
在jsp页面中导入并使用自定义标签,如下:
控制标签体是否执行
孤傲苍狼
运行效果如下:
控制整个jsp页面是否执行
编写一个类实现tag接口,控制doEndTag()方法的返回值,如果这个方法返回EVAL_PAGE,则执行标签余下的jsp页面,如果返回SKIP_PAGE,则不执行余下的jsp。
示例代码如下:
TagDemo2.java
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.Tag;
import javax.servlet.jsp.tagext.TagSupport;
/**
* @author gacl
* TagSupport类实现了Tag接口,TagDemo2继承TagSupport类
*/
public class TagDemo2 extends TagSupport{
/* 重写doEndTag方法,控制jsp页面是否执行
* @see javax.servlet.jsp.tagext.TagSupport#doEndTag()
*/
@Override
public int doEndTag() throws JspException {
//如果这个方法返回EVAL_PAGE,则执行标签余下的jsp页面,如果返回SKIP_PAGE,则不执行余下的jsp
return Tag.SKIP_PAGE;
//return Tag.EVAL_PAGE;
}
}
在WEB-INF目录下的tld文件中添加对该标签处理类的描述,如下:
demo2
me.gacl.web.tag.TagDemo2
empty
在jsp页面中导入并使用自定义标签,如下:
控制jsp页面是否执行
jsp页面的内容1
jsp页面的内容2
运行效果如下:
控制jsp页面内容重复执行
编写一个类实现Iterationtag接口,控制doAfterBody()方法的返回值,如果这个方法返回EVAL_BODY_AGAIN, 则web服务器又执行一次标签体,依次类推,一直执行到doAfterBody方法返回SKIP_BODY,则标签体才不会重复执行。
示例代码如下:
TagDemo3.java
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.IterationTag;
import javax.servlet.jsp.tagext.Tag;
import javax.servlet.jsp.tagext.TagSupport;
public class TagDemo3 extends TagSupport {
int x = 5;
@Override
public int doStartTag() throws JspException {
return Tag.EVAL_BODY_INCLUDE;
}
/* 控制doAfterBody()方法的返回值,
* 如果这个方法返回EVAL_BODY_AGAIN, 则web服务器又执行一次标签体,
* 依次类推,一直执行到doAfterBody方法返回SKIP_BODY,则标签体才不会重复执行。
* @see javax.servlet.jsp.tagext.TagSupport#doAfterBody()
*/
@Override
public int doAfterBody() throws JspException {
x--;
if(x>0){
return IterationTag.EVAL_BODY_AGAIN;
}else{
return IterationTag.SKIP_BODY;
}
}
}
在WEB-INF目录下的tld文件中添加对该标签处理类的描述,如下:
demo3
me.gacl.web.tag.TagDemo3
JSP
在jsp页面中导入并使用自定义标签,如下:
控制页面内容重复执行5次
jsp页面的内容
运行效果如下:
修改jsp页面内容输出
编写一个类实现BodyTag接口,控制doStartTag()方法返回EVAL_BODY_BUFFERED,则web服务器会创建BodyContent对象捕获标签体,然后在doEndTag()方法体内,得到代表标签体的bodyContent对象,从而就可以对标签体进行修改操作。
SUN公司针对BodyTag接口提供了一个默认的实现类BodyTagSupport,BodyTagSupport类中实现了BodyTag接口的所有方法,因此我们可以编写一个类继承BodyTagSupport类,然后再根据需要重写doStartTag方法和doEndTag()方法。
示例代码如下:
TagDemo4.java
import java.io.IOException;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.BodyContent;
import javax.servlet.jsp.tagext.BodyTag;
import javax.servlet.jsp.tagext.BodyTagSupport;
import javax.servlet.jsp.tagext.Tag;
/**
* @author gacl
* BodyTagSupport类实现了BodyTag接口接口,TagDemo4继承 BodyTagSupport类
*/
public class TagDemo4 extends BodyTagSupport {
/* 控制doStartTag()方法返回EVAL_BODY_BUFFERED
* @see javax.servlet.jsp.tagext.BodyTagSupport#doStartTag()
*/
@Override
public int doStartTag() throws JspException {
return BodyTag.EVAL_BODY_BUFFERED;
}
@Override
public int doEndTag() throws JspException {
//this.getBodyContent()得到代表标签体的bodyContent对象
BodyContent bodyContent = this.getBodyContent(); //拿到标签体
String content = bodyContent.getString();
//修改标签体里面的内容,将标签体的内容转换成大写
String result = content.toUpperCase();
try {
//输出修改后的内容
this.pageContext.getOut().write(result);
} catch (IOException e) {
throw new RuntimeException(e);
}
return Tag.EVAL_PAGE;
}
}
在WEB-INF目录下的tld文件中添加对该标签处理类的描述,如下:
demo4
me.gacl.web.tag.TagDemo4
JSP
在jsp页面中导入并使用自定义标签,如下:
修改jsp页面内容输出
xdp_gacl
运行效果如下:
简单标签
由于传统标签使用三个标签接口来完成不同的功能,显得过于繁琐,不利于标签技术的推广, SUN公司为降低标签技术的学习难度,在JSP 2.0中定义了一个更为简单、便于编写和调用的SimpleTag接口来实现标签的功能。
实现SimpleTag接口的标签通常称为简单标签。简单标签共定义了5个方法:
- setJspContext方法
- setParent和getParent方法
- setJspBody方法
- doTag方法(非常重要),简单标签使用这个方法就可以完成所有的业务逻辑
SimpleTag方法介绍
setJspContext方法
用于把JSP页面的pageContext对象传递给标签处理器对象
setParent方法
用于把父标签处理器对象传递给当前标签处理器对象
getParent方法
用于获得当前标签的父标签处理器对象
setJspBody方法
用于把代表标签体的JspFragment对象传递给标签处理器对象
doTag方法
用于完成所有的标签逻辑,包括输出、迭代、修改标签体内容等。在doTag方法中可以抛出javax.servlet.jsp.SkipPageException异常,用于通知WEB容器不再执行JSP页面中位于结束标记后面的内容,这等效于在传统标签的doEndTag方法中返回Tag.SKIP_PAGE常量的情况。
SimpleTag接口方法的执行顺序
当web容器开始执行标签时,会调用如下方法完成标签的初始化:
- WEB容器调用标签处理器对象的setJspContext方法,将代表JSP页面的pageContext对象传递给标签处理器对象。
- WEB容器调用标签处理器对象的setParent方法,将父标签处理器对象传递给这个标签处理器对象。注意,只有在标签存在父标签的情况下,WEB容器才会调用这个方法。
- 如果调用标签时设置了属性,容器将调用每个属性对应的setter方法把属性值传递给标签处理器对象。如果标签的属性值是EL表达式或脚本表达式,则WEB容器首先计算表达式的值,然后把值传递给标签处理器对象。
- 如果简单标签有标签体,WEB容器将调用setJspBody方法把代表标签体的JspFragment对象传递进来。
- 执行标签时WEB容器调用标签处理器的doTag()方法,开发人员在方法体内通过操作JspFragment对象,就可以实现是否执行、迭代、修改标签体的目的。
开发简单标签实现页面逻辑
SUN公司针对SimpleTag接口提供了一个默认的实现类SimpleTagSupport,SimpleTagSupport类中实现了SimpleTag接口的所有方法,因此我们可以编写一个类继承SimpleTagSupport类,然后根据业务需要再重写doTag方法。
控制jsp页面某一部分内容是否执行
编写一个类继承SimpleTagSupport,然后再重写doTag方法,在doTag方法里面不调用jspFrament.invoke方法即可。
示例代码如下:
SimpleTagDemo1.java
import java.io.IOException;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.JspFragment;
import javax.servlet.jsp.tagext.SimpleTagSupport;
/**
* @author gacl
* SimpleTagSupport类实现了SimpleTag接口,
* SampleTagDemo1类继承SimpleTagSupport
*/
public class SimpleTagDemo1 extends SimpleTagSupport {
/* 简单标签使用这个方法就可以完成所有的业务逻辑
* @see javax.servlet.jsp.tagext.SimpleTagSupport#doTag()
* 重写doTag方法,控制标签体是否执行
*/
@Override
public void doTag() throws JspException, IOException {
//得到代表jsp标签体的JspFragment
JspFragment jspFragment = this.getJspBody();
//得到jsp页面的的PageContext对象
//PageContext pageContext = (PageContext) jspFragment.getJspContext();
//调用JspWriter将标签体的内容输出到浏览器
//jspFragment.invoke(pageContext.getOut());
//将标签体的内容输出到浏览器
jspFragment.invoke(null);
}
}
在WEB-INF目录下新建一个simpletag.tld文件,然后在simpletag.tld文件中添加对该标签处理类的描述,如下:
simpletag.tld文件代码如下:
孤傲苍狼开发的SimpleTag自定义标签库
1.0
GaclSimpleTagLibrary
/simpletag
SimpleTag(简单标签)Demo1
demo1
me.gacl.web.simpletag.SimpleTagDemo1
scriptless
在jsp页面中导入并使用自定义标签,如下:
用简单标签控制标签体是否执行
孤傲苍狼
运行效果如下:
控制jsp页面内容重复执行
编写一个类继承SimpleTagSupport,然后再重写doTag方法,在doTag方法里面重复调用jspFrament.invoke方法即可。
示例代码如下:
SimpleTagDemo2.java
import java.io.IOException;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.JspFragment;
import javax.servlet.jsp.tagext.SimpleTagSupport;
/**
* @author gacl
* SimpleTagSupport类实现了SimpleTag接口,
* SampleTagDemo2类继承SimpleTagSupport
*/
public class SimpleTagDemo2 extends SimpleTagSupport {
/* 简单标签使用这个方法就可以完成所有的业务逻辑
* @see javax.servlet.jsp.tagext.SimpleTagSupport#doTag()
* 重写doTag方法,控制标签执行5次
*/
@Override
public void doTag() throws JspException, IOException {
// 得到代表jsp标签体的JspFragment
JspFragment jspFragment = this.getJspBody();
for (int i = 0; i < 5; i++) {
// 将标签体的内容输出到浏览器
jspFragment.invoke(null);
}
}
}
在WEB-INF目录下的simpletag.tld文件中添加对该标签处理类的描述,如下:
demo2
me.gacl.web.simpletag.SimpleTagDemo2
scriptless
在jsp页面中导入并使用自定义标签,如下:
用简单标签控制标签体执行5次
孤傲苍狼
运行效果如下:
修改jsp页面内容输出
编写一个类继承SimpleTagSupport,然后再重写doTag方法,在doTag方法调用jspFrament.invoke方法时,让执行结果写一个自定义的缓冲中即可,然后开发人员可以取出缓冲的数据修改输出。
示例代码如下:
SimpleTagDemo3.java
import java.io.IOException;
import java.io.StringWriter;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.JspFragment;
import javax.servlet.jsp.tagext.SimpleTagSupport;
/**
* @author gacl
* SimpleTagSupport类实现了SimpleTag接口,
* SampleTagDemo3类继承SimpleTagSupport
*/
public class SimpleTagDemo3 extends SimpleTagSupport {
/* 简单标签使用这个方法就可以完成所有的业务逻辑
* @see javax.servlet.jsp.tagext.SimpleTagSupport#doTag()
* 重写doTag方法,修改标签体里面的内容,将标签体的内容转换成大写
*/
@Override
public void doTag() throws JspException, IOException {
// 得到代表jsp标签体的JspFragment
JspFragment jspFragment = this.getJspBody();
StringWriter sw = new StringWriter();
//将标签体的内容写入到sw流中
jspFragment.invoke(sw);
//获取sw流缓冲区的内容
String content = sw.getBuffer().toString();
content = content.toUpperCase();
PageContext pageContext = (PageContext) this.getJspContext();
//将修改后的content输出到浏览器中
pageContext.getOut().write(content);
}
}
在WEB-INF目录下的simpletag.tld文件中添加对该标签处理类的描述,如下:
demo3
me.gacl.web.simpletag.SimpleTagDemo3
scriptless
在jsp页面中导入并使用自定义标签,如下:
用简单标签修改jsp页面内容输出
gacl_xdp
运行效果如下:
控制整个jsp页面是否执行
编写一个类继承SimpleTagSupport,然后再重写doTag方法,在doTag方法抛出SkipPageException异常即可,jsp收到这个异常,将忽略标签余下jsp页面的执行。
示例代码如下:
SimpleTagDemo4.java
import java.io.IOException;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.SkipPageException;
import javax.servlet.jsp.tagext.SimpleTagSupport;
/**
* @author gacl
* SimpleTagSupport类实现了SimpleTag接口,
* SampleTagDemo4类继承SimpleTagSupport
*/
public class SimpleTagDemo4 extends SimpleTagSupport {
/* 简单标签使用这个方法就可以完成所有的业务逻辑
* @see javax.servlet.jsp.tagext.SimpleTagSupport#doTag()
* 重写doTag方法,控制标签余下的Jsp不执行
*/
@Override
public void doTag() throws JspException, IOException {
//抛出一个SkipPageException异常就可以控制标签余下的Jsp不执行
throw new SkipPageException();
}
}
在WEB-INF目录下的simpletag.tld文件中添加对该标签处理类的描述,如下:
demo4
me.gacl.web.simpletag.SimpleTagDemo4
empty
在jsp页面中导入并使用自定义标签,如下:
用简单标签控制标签余下的Jsp不执行
孤傲苍狼
白虎神皇
运行效果如下:
简单标签开发的一些注意细节
标签类编写细节
开发标签类时,不要直接去实现SimpleTag接口,而是应该继承SimpleTagSupport类,SimpleTagSupport类是SimpleTag接口的一个默认实现类,通过继承SimpleTagSupport类,就可以直接使用SimpleTagSupport类已经实现的那些方法,如果SimpleTagSupport类的方法实现不满足业务要求,那么就可以根据具体的业务情况将相应的方法进行重写。
tld文件中标签体类型设置细节
我们开发好一个简单标签后,需要在tld文件中添加对该标签的描述,例如:
demo2
me.gacl.web.simpletag.SimpleTagDemo2
scriptless
开发好一个标签后,在tld文件中使用来描述一个标签,描述的内容包括标签名(name),标签处理器类(tag-class),标签体的内容(body-content)。
tld文件中有四种标签体(body-content)类型 :empty、JSP、scriptless、tagdependent
简单标签标签体的细节注意问题: 在简单标签(SampleTag)中标签体body-content的值只允许是empty、scriptless、tagdependent,不允许设置成JSP,如果设置成JSP就会出现异常:
The TLD for the class me.gacl.web.simpletag.SimpleTagDemo1 specifies an invalid body-content (JSP) for a SimpleTag
body-content的值如果设置成empty,那么就表示该标签没有标签体,如果是设置成scriptless,那么表示该标签是有标签体的,但是标签体中的内容不可以是,例如:
孤傲苍狼
否则运行标签时就会出现如下错误:
Scripting elements ( <%!, <jsp:declaration, <%=, <jsp:expression, <%, <jsp:scriptlet ) are disallowed here
tld文件中标签库的uri设置细节
如果在一个项目中使用或者开发了多个标签库,例如:
那么标签库的uri不能设置成相同的,否则在Jsp页面中通过uri引用标签库时就不知道引用哪一个标签库了,如果真的有那么巧,两个标签库的uri是刚好一样的,如下图所示:
那么在jsp页面中引用标签库时如果""这样引用,那么就无法判断当前引用的标签库到底是gacl.tld标签库中的标签还是simpletag.tld标签库中的标签,因为两个标签库的uri刚好都是"/gacl",在两个标签库的引用uri一样的情况下,为了能够在jsp中区别到底引用的是哪个标签库,可以换一种引用方式:,使用taglib指令引入标签库时,taglib指令的uri属性指定为标签库的tld文件目录,这样就可以区别开了,例如:
引用gacl.tld标签库:、
引用simpletag.tld标签库:
所以当在项目中引用了多个标签库,如果标签库的uri刚好是一样的,就可以用这种方式解决。
简单标签开发步骤总结
1、编写一个类继承SimpleTagSupport类,然后根据业务需要重写SimpleTagSupport类中已经实现了的方法,一般情况下只需要重写doTag()方法即可。
2、在WEB-INF目录下创建一个tld文件,在tld文件中添加对该标签的描述。tld文件不一定放在WEB-INF目录下,也可以放在别的目录,习惯是放在WEB-INF目录下。