JSP引擎调用标签处理器的基本原理
1、 Web容器执行自定义标签开始标记时,会调用标签处理器的doStartTag()。可以通过该方法返回的不同的值来控制后续的行为:
是否执行标签体中的内容。
是直接输出标签体的执行结果,还是将标签体的执行结果输出到一个缓冲区对象中,从而可以让后续的时间方法对标签体的执行结果进行修改和控制其输出。
2、Web容器执行完开始标记后,执行标签体,标签体执行完成后调用doAfterBody()方法。该方法用于标签体执行完后的行为,这个方法可以返回不同的值来控制是否再次重复执行标签体。
3、Web容器执行完自定义标签体后,会执行自定义标签的结束标记,此时会执行doEndTag()方法。用于处理遇到结束标记这个事件,通过不同的值来控制Web容器是否执行JSP页面中位于结束标记后面的内容。
8.3自定义标签API
8.3.1JspTag接口
是所有自定义标签的父接口,Tag 接口和SimpeTag 是其直接子接口。
8.3.2Tag接口
Tag接口是传统标签的父接口,定义了两个重要方法doStartTag()、doEndTag() ;四个常量(EVAL_BODY_INCLUDE、SKIP_BODY、EVAL_PAGE、SKIP_PAGE )。
1、调用doStartTag() 方法执行后向Web容器返回常EVAL_BODY_INCLUDE、SKIP_BODY 。返回EVAL_BODY_INCLUDE 则执行自定义标签的标签体;返回SKIP_BODY则忽略自定义标签的标签体,直接解释执行自定义标签的结果标记。
2、调用doEndTag() 方法、执行后向Web容器返回常量EVAL_PAGE、SKIP_PAGE 。返回EVAL_PAGE 则执行JSP页面位于结束标记后面的JSP代码、返回SKIP_PAGE则忽略JSP页面中位于结束标记后面的所有内容,例如<jsp:forward> 标签。
8.3.3 IterationTag接口
IterationTag继承Tag接口,并增加了doAfterBody() 方法和EVAL_BODY_AGAIN 常量。能通知Web容器是否重复执行标签体内容。当调用doAfterBody() 方法返回EVAL_BODY_AGAIN 时,会重复执行标签,知道返回SKIP_BODY,Web容器才会处理标签的结束标记和调用doEndTag() 。
8.3.4BodyTag接口
BodyTag接口继承自IterationTag 接口,增加2个方法setBodyContent()、doInitBody() 方法和EVAL_BODY_BUFFERED 常量。当doStartTag()方法返回EVAL_BODY_BUFFERED 常量时,Web容器会创建专门用于捕获标签体运行结果的BodyContent 对象,然后调用标签处理器的setBodyContent() 将BodyContent 对象传入标签处理器,Web容器将标签体的执行结果写入BodyContent 对象中。在标签处理器的后续事件方法中,可以通过保存的BodyContent 的对象的引用来获取标签体的执行结果,然后调用BodyContent对象特有的方法对BodyContent对象的内容进行修改和控制。
注:JSP API提供BodyTag接口的实现类BodyTagSupport 。BodyTagSupport 类实现的BodyTagSupport类的doStartTag方法的返回值为EVAL_BODY_BUFFERED。doAfterBody() 返回值为SKIP_BODY,doEndTag() 返回值为EVAL_PAGE 。
8.3.5SimpleTag接口
SimpleTag接口为JSP2.0新增标签接口,在SimpleTag之中定义了一个用于处理标签逻辑的doTag()方法,该方法在Web容器执行自定义标签时调用,而且只调用一次即完成传统标签的是否执行标签体、迭代标签等功能。
SimpleTagSupport类是SimpleTag接口的实现类。
8.4自定义标签基本使用
8.4.1Tag接口
1、Tag接口静态常量EVAL_BODY_INCLUDE、SKIP_BODY、EVAL_PAGE、SKIP_PAGE
2、 方法:setPageContext()、setParent()、doStartTag()、doEndTag()、release()
public void setPageContext(PageContext pc); public void setParent(Tag tag); public int doStartTag(); public int doEndTag(); public void release();
8.4.1.1显示用户信息
public class DisplayUserInfoTag extends TagSupport { public int doStartTag() throws JspException { HttpSession session = pageContext.getSession(); String username = (String) session.getAttribute("username"); if (username != null) { return EVAL_BODY_INCLUDE; } else { return SKIP_BODY; }}}
Tld文件<taglib> <jsp-version>1.0</jsp-version> <tlib-version>1.0</tlib-version> <short-name>wangtongtaglib</short-name> <uri>/wangtongtaglib</uri> <tag> <name>displayuserinfo</name> <tag-class>tag.DisplayUserInfoTag</tag-class> <body-content>JSP</body-content> </tag> </taglib>JSP文件<wangtong:displayuserinfo> username:${username} </wangtong:displayuserinfo>8.4.1.2是否执行JSP页面内容public class ValidateTag extends TagSupport { @Override public int doEndTag() throws JspException { HttpServletRequest req = (HttpServletRequest) pageContext.getRequest(); String referrer = req.getHeader("referer"); String siteParnt = "http://" + req.getServerName(); if (referrer != null && referrer.startsWith(siteParnt)) { return EVAL_PAGE; } else { try { pageContext.getOut().write("对不起,您访问方式不合法"); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } return SKIP_PAGE; }}}
8.5自定义标签属性
8.5.1静态属性应用public class ValidateTag2 extends TagSupport { private String url; public void setUrl(String url) { this.url = url; } public int doEndTag() throws JspException { HttpServletRequest req = (HttpServletRequest) pageContext.getRequest(); String referrer = req.getHeader("referer"); String siteParnt = "http://" + req.getServerName(); if (referrer != null && referrer.startsWith(siteParnt)) { return EVAL_PAGE; } else { try { pageContext.forward(url); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } return SKIP_PAGE; } } }?
Tld:<tag> <name>validatetag2</name> <tag-class>tag.ValidateTag2</tag-class> <body-content>empty</body-content> <attribute> <name>url</name> <required>true</required> </attribute> </tag>
JSP页面<wangtong:validatetag2 url="error.html" />
8.5.2动态属性值应用
可以在attribute元素的rtexprvalue 子元素指定JSP是否为动态元素。public class DynamicAttributeValueTag extends TagSupport { private Date birthday; public void setBirthday(Date birthday) { this.birthday = birthday; } public int doStartTag() throws JspException { JspWriter out = pageContext.getOut(); try { out.write(DateFormat.getInstance().format(birthday)); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); }return super.doStartTag();}}
Tld配置文件:<tag> <name>dynamicAttributeValue</name> <tag-class>tag.DynamicAttributeValueTag</tag-class> <body-content>empty</body-content> <attribute> <name>birthday</name> <required>true</required> <rtexprvalue>true</rtexprvalue> </attribute> </tag>
JSP文件:<wangtong:dynamicAttributeValue birthday="<%=new Date() %>"/>
8.5.3动态属性应用
动态属性指在标签处理器类和TLD文件中没有预先声明的属性,但是在JSP页面去可以为这些标签设置这些属性。实现动态属性必须满足下例条件:
1、 继承javax.servlet.jsp.tagext.DynamicAttributes 接口,实现setDynamicAttribute(url,localName,value) 方法。
2、 在TLD文件中使用<dynamic-attributes> 声明标签支持动态属性
8.6迭代标签
8.6.1IterationTag接口
IterationTag接口继承自Tag接口,并增加doAfterBody ()方法和EVAL_BODY_AGAIN 常量。
8.6.2IterationTag实例public class IterateTag extends TagSupport { private String name; private String[] items; private int i = 1; public void setName(String name) { this.name = name; } public void setItems(String[] items) { this.items = items; } public void setI(int i) { this.i = i; } @Override public int doStartTag() throws JspException { if (items != null && items.length > 0) { pageContext.setAttribute("name", items[0]); return EVAL_BODY_INCLUDE; } else { return SKIP_BODY; } } @Override public int doAfterBody() throws JspException { if (i < items.length) { pageContext.setAttribute(name, items[i]); i++; return EVAL_BODY_AGAIN;// 循环 } else { return SKIP_BODY; }}}<tag> <name>iterate</name> <tag-class>tag.IterateTag</tag-class> <body-content>JSP</body-content> <attribute> <name>name</name> <required>true</required> </attribute> <attribute> <name>items</name> <required>true</required> <rtexprvalue>true</rtexprvalue> </attribute> </tag>
JSP:<%String[] books = { "book1", "book2", "book3" };%> <wangtong:iterate name="bookname" items="<%=books%>"> %{name}<br /> </wangtong:iterate>
8.7自定义标签定义JSP脚本变量
8.7.1指定变量名称<tag> <name>scriptvariableiterate</name> <tag-class>tag.IterateTag</tag-class> <body-content>JSP</body-content> <!-- 指定变量名称 --> <variable> <name-given>bookname</name-given> </variable> <attribute> <name>name</name> <required>true</required> </attribute> <attribute> <name>items</name> <required>true</required> <rtexprvalue>true</rtexprvalue> </attribute> </tag><wangtong:scriptvariableiterate name="bookname" items="<%=books%>"> <%=bookname %> </wangtong:scriptvariableiterate>
注:JSP中的bookname变量没有明确声明,时通过tld配置文件的variable 标签进行定义的
8.7.2指定属性名称<tag> <name>scriptattributeiterate</name> <tag-class>tag.IterateTag</tag-class> <body-content>JSP</body-content> <!-- 指定属性名称 --> <variable> <name-from-attribute>name</name-from-attribute> </variable> <attribute> <name>name</name> <required>true</required> </attribute> <attribute> <name>items</name> <required>true</required> <rtexprvalue>true</rtexprvalue> </attribute> </tag><wangtong:scriptattributeiterate name="name" items="<%=books%>"> <%=name %> </wangtong:scriptattributeiterate>
注:在使用name-given对变量进行声明,比较死板,只能使用指定的变量名称,故JSP API中提供了通过的指定属性的方法实现类似机制。需要将name-given修改为name-from-attribute。
8.7.3Variable子元素含义
Description:表述信息
Name-given:指定一个固定的脚本变量名
Name-form-attribute: 通过属性指定变量名称,标签易用性较name-given强
Variable-class:指定脚本变量的java类型
Scope NESTED:从标签开始到标签结束
AT _BEGIN 从标签开始到JSP页面结束
AT_END 从标签技术到JSP页面结束8.7.4用TagExtraInfo 类定义脚本变量
可以使用TagExtraInfo类替代上面再tld文件中配置variable元素实现定义脚本变量。
TagExtraInfo类在使用时可能需要以下2个类:VariableInfo、TagData。
VariableInfo:用于封装脚本变量的定义信息,并提供JSP页面获得这些脚本变量的定义信息的方法。public VariableInfo(String varname,String classname,boolean declare,int scope);与variable元素的子元素一一对应。
TagData:用来获取标签的属性及属性值。public String getAttributeString(String attributeName);
TagExtraInfo:JSP引擎通过TagExtraInfo类获得JSP脚本变量的定义信息,它是一个抽象类需要实现getVariableInfo(TagData data)方法。并且需要在tld文件中及进行注册,使用<tei-class>标签。public class MyTagExtraInfo extends TagExtraInfo { @Override public VariableInfo[] getVariableInfo(TagData data) { return new VariableInfo[]{ new VariableInfo(data.getAttributeString("name"), "java.lang.String", true, VariableInfo.AT_BEGIN) }; } }<tag> <name>scripttagextrainfoiterate</name> <tag-class>tag.IterateTag</tag-class> <!-- 指定TagExtraInfo实现类 --> <tei-class>tag.MyTagExtraInfo</tei-class> <body-content>JSP</body-content> <attribute> <name>name</name> <required>true</required> </attribute> <attribute> <name>items</name> <required>true</required> <rtexprvalue>true</rtexprvalue> </attribute> </tag>8.7.5userBean标签的实现public class UseBeanTag extends TagSupport { private String name; private String type; private String scope; private int iScope; public void setName(String name) { this.name = name; } public void setType(String type) { this.type = type; } public void setScope(String scope) { this.scope = scope; } public void setIScope(int scope) { iScope = scope; } @Override public int doStartTag() throws JspException { if ("page".equals(scope)) { iScope = pageContext.PAGE_SCOPE; } else if ("request".equals(scope)) { iScope = pageContext.REQUEST_SCOPE; } else if ("session".equals(scope)) { iScope = pageContext.SESSION_SCOPE; } else if ("application".equals(scope)) { iScope = pageContext.APPLICATION_SCOPE; } try { Object obj = Class.forName(type).newInstance(); pageContext.setAttribute(name, obj, iScope); } catch (Exception e) { e.printStackTrace(); } return EVAL_BODY_INCLUDE; }} public class UseBeanTagExtraInfo extends TagExtraInfo { @Override public VariableInfo[] getVariableInfo(TagData data) { return new VariableInfo[] { new VariableInfo(data .getAttributeString("name"), data.getAttributeString("type"), true, VariableInfo.AT_BEGIN) }; } }<tag> <name>useBean</name> <tag-class>tag.UseBeanTag</tag-class> <tei-class>tag.UseBeanTagExtraInfo</tei-class> <body-content>JSP</body-content> <attribute> <name>name</name> <required>true</required> </attribute> <attribute> <name>type</name> <required>true</required> </attribute> <attribute> <name>scope</name> <required>true</required> </attribute> </tag><wangtong:useBean name="date" scope="page" type="java.util.Date"/> <%=date.getYear()+1900%>year<%=date.getMonth()+1%>month <%=date.getDate()%>date
8.8处理标签体内容
有时候需要对标签体的执行结果进行修改后再输出,在JSP中定义了一个BodyTag 接口支持该功能。
BodyTag接口继承自IterationTag接口,并增加setBodyContent(),doInitBody() 两个方法和一个常量EVAL_BODY_BUFFERED 。
当doStartTag()方法返回值为EVAL_BODY_BUFFERED 时,JSP引擎创建BodyContent 对象并以此调用setBodyContent()和doInitBody()方法。然后JSP执行标签体内容,并将执行结果输出到BodyContent对象中,然后调用doAfterBody()。当doAfterBody()返回EVAL_BODY_AGAIN时,继续执行标签体内容,知道返回SKIP_BODY为止。
BodyTag常用方法:public void setBodyContent(BodyContent content); public void doInitBody();
BodyContent类:继承自JspWriter类,增加用于保护数据的缓冲区。public abstract String getString();
以字符串形式返回BodyContent缓冲区内容public abstract Reader getReader();
返回一个连接到BodyContent对象缓冲区的Reader对象public void clearBody();
清空BodyContent对象缓冲区内容public JspWriter getEnclosingWriter();
得到BodyContent对象关联的JSPWriter对象public abstract void writeOut(Writer out);
将BodyContent对象缓冲区内容写入到指定输出流
8.8.1BodyTag接口简单应用public class BodyTag extends BodyTagSupport { BodyContent bodyContent; @Override public int doEndTag() throws JspException { String content = bodyContent.getString();// 得到标签体内容 String newContent = "<a href = 'http://'" + content + "'>" + content + "</a>"; JspWriter out = bodyContent.getEnclosingWriter();// 获取JSPWriter对象 try { out.write(newContent); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } return EVAL_PAGE; } @Override public void setBodyContent(BodyContent b) { bodyContent = b; }}<wangtong:bodytag>wangtong</wangtong:bodytag>