在JSP2.0 中,對于自定義的標簽有兩種實(shí)現方法,實(shí)現接口或者繼承現有的類(lèi)
如下圖,標注藍色的是接口,其它是標簽類(lèi)(SimpleTagSupport只在JSP2.0中才有)
在以上接口和類(lèi)中,定義了一些靜態(tài)常量,如下:
Tag 中定義:
SKIP_BODY = 0; // 不處理標簽體,直接調用doEndTag()方法
EVAL_BODY_INCLUDE = 1; // 解析標簽體,但繞過(guò) doInitBody () 和 setBodyContent () 方法
SKIP_PAGE = 5; // 不解析標簽后面的JSP內容
EVAL_PAGE = 6; // 解析標簽后,繼續解析標簽后面的JSP內容
IterationTag 中定義:
EVAL_BODY_AGAIN = 2;
BodyTag 中定義:
EVAL_BODY_TAG = 2; // deprecated
EVAL_BODY_BUFFERED = 2; //
特別的,對于EVAL_BODY_AGAIN和EVAL_BODY_BUFFERED:
在doAferBody中返回SKIP_BODY,表示終止標記正文處理;若返回的是 EVAL_BODY_BUFFERED ,將會(huì )再一次調用doAferBody方法,重新處理標記正文,直到返回SKIP_BODY為止。 // ①
下面是自定義tag的執行過(guò)程(由上至下),對于以上各常量的實(shí)際運用為:
注意其中的 doInitBody/setBodyContent 方法在自定義標簽實(shí)現了 BodyTag 接口或繼承BodyTagSupport才可以使用
| Tag 方法 | 可返回的靜態(tài)常量 |
| doStartTag | SKIP_BODY 、EVAL_BODY_INCLUDE、 EVAL_BODY_AGAIN/EVAL_BODY_BUFFERED |
| doInitBody | 做標簽一些初始化工作,無(wú)返回值 |
| setBodyContent | 在 doInitBody 之后執行,使用setBodyContent得到JSP頁(yè)面中標簽體之間內容 |
| doAfterBody | 最終必須返回SKIP_BODY ,否則可能導致OutOfMemoryError,可參考上面① |
| doEndTag | SKIP_PAGE/EVAL_PAGE |
附 ① 示例代碼如下:
public int doAfterBody() throws JspException {
try {
this.pageContext.getOut().write("<br>");
} catch (IOException e) {
e.printStackTrace();
}
if(cou>1){
cou--;
return this.EVAL_BODY_AGAIN;
}else{
return this.SKIP_BODY; // 最終必須返回SKIP_BODY
}
}
自定義標簽的開(kāi)發(fā)包括:
1. 開(kāi)發(fā)標簽的處理程序(java類(lèi))
2. .tld 文件中指定標簽使用的類(lèi)
3. 在web.xml中指定JSP中使.tld(標簽庫描述文件)文件的位置。
在.tld文件中
<tag>
<name>out</name>
<tag-class>org.apache.taglibs.standard.tag.el.core.OutTag</tag-class>
<body-content>JSP</body-content>
<attribute>
<name>value</name>
<required>true</required>
<rtexprvalue>false</rtexprvalue>
</attribute>
</tag>
body-content :
根據web-jsptaglibrary_2_0.xsd(位于servlet-api.jar包($TOMCAT_HOME\common\lib)中的\javax\servlet\resources下,其中web.xml驗證時(shí)所需要的xsd文件都位于此resources目錄下), body-content 的值有下面4種:
| <xsd:enumeration value="tagdependent"/> <xsd:enumeration value="JSP"/> <xsd:enumeration value="empty"/> <xsd:enumeration value="scriptless"/> |
tagdependent : 標簽體內容 直接被寫(xiě)入BodyContent,由自定義標簽類(lèi)來(lái)進(jìn)行處理,而不被JSP容器解釋,
如下:
<test:myList>
select name,age from users
</test:myList>
JSP : 接受所有JSP語(yǔ)法,如定制的或內部的tag、scripts、靜態(tài)HTML、腳本元素、JSP指令和動(dòng)作。如:
<my:test>
<%=request.getProtocol()%> // ②
</my:test>
具體可參考后面附源碼。
empty : 空標記,即起始標記和結束標記之間沒(méi)有內容。
下面幾種寫(xiě)法都是有效的,
<test:mytag />
<test:mytag uname="Tom" />
<test:mytag></test:mytag>
scriptless : 接受文本、EL和JSP動(dòng)作。如上述②使用<body-content> scriptless </body-content> 則報錯,具體可參考后面附源碼。
rtexprvalue:
由請求時(shí)表達式來(lái)指定屬性的值,默認為false,如下必須設置為true:
<test:welcome uname="<%=request.getParameter("username") %>" />
附body-content為 JSP/scriptless 時(shí)標簽體可以接受的代碼(jasper-compiler.jar包 ($TOMCAT_HOME\common\lib)中 的\org\apache\jasper\compiler\Parser.java中):
JSP:
private void parseElements(Node parent)
throws JasperException
{
if( scriptlessCount > 0 ) {
// vc: ScriptlessBody
// We must follow the ScriptlessBody production if one of
// our parents is ScriptlessBody.
parseElementsScriptless( parent );
return;
}
start = reader.mark();
if (reader.matches("<%--")) {
parseComment(parent);
} else if (reader.matches("<%@")) {
parseDirective(parent);
} else if (reader.matches("<jsp:directive.")) {
parseXMLDirective(parent);
} else if (reader.matches("<%!")) {
parseDeclaration(parent);
} else if (reader.matches("<jsp:declaration")) {
parseXMLDeclaration(parent);
} else if (reader.matches("<%=")) {
parseExpression(parent);
} else if (reader.matches("<jsp:expression")) {
parseXMLExpression(parent);
} else if (reader.matches("<%")) {
parseScriptlet(parent);
} else if (reader.matches("<jsp:scriptlet")) {
parseXMLScriptlet(parent);
} else if (reader.matches("<jsp:text")) {
parseXMLTemplateText(parent);
} else if (reader.matches("${")) {
parseELExpression(parent);
} else if (reader.matches("<jsp:")) {
parseStandardAction(parent);
} else if (!parseCustomTag(parent)) {
checkUnbalancedEndTag();
parseTemplateText(parent);
}
}
Scriptless:
private void parseElementsScriptless(Node parent)
throws JasperException
{
// Keep track of how many scriptless nodes we‘ve encountered
// so we know whether our child nodes are forced scriptless
scriptlessCount++;
start = reader.mark();
if (reader.matches("<%--")) {
parseComment(parent);
} else if (reader.matches("<%@")) {
parseDirective(parent);
} else if (reader.matches("<jsp:directive.")) {
parseXMLDirective(parent);
} else if (reader.matches("<%!")) {
err.jspError( reader.mark(), "jsp.error.no.scriptlets" );
} else if (reader.matches("<jsp:declaration")) {
err.jspError( reader.mark(), "jsp.error.no.scriptlets" );
} else if (reader.matches("<%=")) {
err.jspError( reader.mark(), "jsp.error.no.scriptlets" );
} else if (reader.matches("<jsp:expression")) {
err.jspError( reader.mark(), "jsp.error.no.scriptlets" );
} else if (reader.matches("<%")) {
err.jspError( reader.mark(), "jsp.error.no.scriptlets" );
} else if (reader.matches("<jsp:scriptlet")) {
err.jspError( reader.mark(), "jsp.error.no.scriptlets" );
} else if (reader.matches("<jsp:text")) {
parseXMLTemplateText(parent);
} else if (reader.matches("${")) {
parseELExpression(parent);
} else if (reader.matches("<jsp:")) {
parseStandardAction(parent);
} else if (!parseCustomTag(parent)) {
checkUnbalancedEndTag();
parseTemplateText(parent);
}
scriptlessCount--;
}
由上面可以看出,局限性比較小,在body-content可以使用 Scriptless 的地方都可以用 JSP 代替,反之則不可。
聯(lián)系客服