1、創(chuàng)建工程
新建一個java web工程:
選擇工程存儲位置,輸入工廠名“JavaWebSenior”:
工程結(jié)構(gòu)如下:
在WEB-INF
目錄下新建classes和lib目錄如下:
設置編譯后class文件的輸出路徑,配置為前面創(chuàng)建的classes目錄灭贷。File->Project Structure:
設置依賴jar包的存放位置削茁,配置為前面創(chuàng)建的lib目錄(選擇Jar Directory):
添加tomcat服務器:
這里之前已經(jīng)在IDEA中添加了Tomcat服務器,這里只需要選中之前添加的Tomcat服務器粟害,并定義一個別名(JavaWebSeniorTomcat)即可:
然后蕴忆,在上面的對話框中,將當前的工程添加到tomcat運行悲幅。點擊加號套鹅,選擇Artifact...
:
效果如下:
然后,確定汰具,即可看到當前項目已經(jīng)添加到了tomcat運行卓鹿。
修改jsp代碼,如下:
這時候留荔,啟動tomcat:
就會看到瀏覽器中自動打開http://localhost:8080
吟孙,效果如下:
2、自定義標簽入門
使用自定義標簽存谎,可以去掉jsp頁面上的java代碼拔疚。
2.1 入門示意
這里首先要將tomcat的lib目錄引入到項目的依賴,不然的話既荚,好多java web相關(guān)的沒法使用稚失。
首先,寫一個Java類恰聘,實現(xiàn)自定義標簽的內(nèi)容句各。這里實現(xiàn)一個自定義標簽,用來展現(xiàn)客戶端的ip晴叨。
com.test.selfdefine.tag.ShowIpAddressTag
:
package com.test.selfdefine.tag;
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;
import java.io.IOException;
/**
* Created by chengxia on 2019/10/11.
*/
public class ShowIpAddressTag implements Tag {
//接收傳遞進來的PageContext對象
private PageContext pageContext;
@Override
public void setPageContext(PageContext pageContext) {
this.pageContext = pageContext;
System.out.println("setPageContext(PageContext pageContext) function executing...");
}
@Override
public void setParent(Tag tag) {
//什么也不做
}
@Override
public Tag getParent() {
return null;
}
@Override
public int doStartTag() throws JspException {
System.out.println("doStartTag() function executing...");
HttpServletRequest request =(HttpServletRequest) pageContext.getRequest();
JspWriter out = pageContext.getOut();
String ip = request.getRemoteAddr();
try {
//這里輸出的時候會拋出IOException異常
out.write(ip);
} catch (IOException e) {
//捕獲IOException異常后繼續(xù)拋出
throw new RuntimeException(e);
}
return 0;
}
@Override
public int doEndTag() throws JspException {
System.out.println("doEndTag() function executing...");
return 0;
}
@Override
public void release() {
System.out.println("release() function executing...");
}
}
新建一個自定義標簽的配置文件凿宾。
WEB-INF/selfdefine.tld
:
<?xml version="1.0" encoding="utf-8"?>
<taglib xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd"
version="2.1">
<!-- description用來添加對taglib(標簽庫)的描述 -->
<description>Try Selfdefine Tag</description>
<!--taglib(標簽庫)的版本號 -->
<tlib-version>1.0</tlib-version>
<!-- 這里里面不能有空格 -->
<short-name>SelfdedineTagLibrary</short-name>
<!--
為自定義標簽庫設置一個uri,uri以/開頭兼蕊,/后面的內(nèi)容隨便寫初厚,如這里的/tagtest ,
在Jsp頁面中引用標簽庫時孙技,需要通過uri找到標簽庫
在Jsp頁面中就要這樣引入標簽庫:<%@taglib uri="/tagtest" prefix="tagtest"%>
-->
<uri>/tagtest</uri>
<!--一個taglib(標簽庫)中包含多個自定義標簽产禾,每一個自定義標簽使用一個tag標記來描述 -->
<!-- 一個tag標記對應一個自定義標簽 -->
<tag>
<description>用來輸出客戶端的IP地址</description>
<!--
為標簽處理器類配一個標簽名,在Jsp頁面中使用標簽時是通過標簽名來找到要調(diào)用的標簽處理器類的
通過viewIP就能找到對應的me.gacl.web.tag.ViewIPTag類
-->
<name>viewIP</name>
<!-- 標簽對應的處理器類-->
<tag-class>com.test.selfdefine.tag.ShowIpAddressTag</tag-class>
<body-content>empty</body-content>
</tag>
</taglib>
到這里牵啦,我們就完成了一個自定義標簽的定義亚情。在后面的jsp頁面中,可以引用這個自定義標簽哈雏。
index.jsp
:
<%--
Created by IntelliJ IDEA.
User: chengxia
Date: 2019/10/10
Time: 8:43 AM
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!-- 使用taglib指令引用gacl標簽庫楞件,標簽庫的前綴(prefix)可以隨便設置衫生,如這里設置成 prefix="xdp" -->
<%@taglib uri="/tagtest" prefix="tagtest"%>
<html>
<head>
<title>Self Define Tag Test</title>
</head>
<body>
<h1>Get IP Address using different ways:</h1>
你的IP地址是(使用java代碼獲取輸出):
<%
//在jsp頁面中使用java代碼獲取客戶端IP地址
String ip = request.getRemoteAddr();
out.write(ip);
%>
<hr/>
你的IP地址是(使用自定義標簽獲取輸出):
<%--使用自定義標簽viewIP --%>
<tagtest:viewIP/>
</body>
</html>
完成之后,工程的目錄結(jié)構(gòu)如下土浸。
啟動Tomcat罪针,瀏覽器自動打開index.jsp
。
2.2 自定義標簽的執(zhí)行流程
我在MacOS上栅迄,用IDEA 2017進行開發(fā)站故,Tomcat 8。在我的機器上毅舆,打開/Users/chengxia/Library/Caches/IntelliJIdea2017.1/tomcat/Unnamed_HelloWorld/work/Catalina/localhost/ROOT/org/apache/jsp
目錄西篓,可以看到index.jsp
編譯后的java文件。
/Users/chengxia/Library/Caches/IntelliJIdea2017.1/tomcat/Unnamed_JavaWebSenior/work/Catalina/localhost/ROOT/org/apache/jsp/index_jsp.java
:
/*
* Generated by the Jasper component of Apache Tomcat
* Version: Apache Tomcat/8.0.53
* Generated at: 2019-10-12 00:33:55 UTC
* Note: The last modified time of this file was set to
* the last modified time of the source file after
* generation to assist with modification tracking.
*/
package org.apache.jsp;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;
public final class index_jsp extends org.apache.jasper.runtime.HttpJspBase
implements org.apache.jasper.runtime.JspSourceDependent,
org.apache.jasper.runtime.JspSourceImports {
private static final javax.servlet.jsp.JspFactory _jspxFactory =
javax.servlet.jsp.JspFactory.getDefaultFactory();
private static java.util.Map<java.lang.String,java.lang.Long> _jspx_dependants;
static {
_jspx_dependants = new java.util.HashMap<java.lang.String,java.lang.Long>(1);
_jspx_dependants.put("/WEB-INF/selfdefine.tld", Long.valueOf(1570838325000L));
}
private static final java.util.Set<java.lang.String> _jspx_imports_packages;
private static final java.util.Set<java.lang.String> _jspx_imports_classes;
static {
_jspx_imports_packages = new java.util.HashSet<>();
_jspx_imports_packages.add("javax.servlet");
_jspx_imports_packages.add("javax.servlet.http");
_jspx_imports_packages.add("javax.servlet.jsp");
_jspx_imports_classes = null;
}
private org.apache.jasper.runtime.TagHandlerPool _005fjspx_005ftagPool_005ftagtest_005fviewIP_005fnobody;
private volatile javax.el.ExpressionFactory _el_expressionfactory;
private volatile org.apache.tomcat.InstanceManager _jsp_instancemanager;
public java.util.Map<java.lang.String,java.lang.Long> getDependants() {
return _jspx_dependants;
}
public java.util.Set<java.lang.String> getPackageImports() {
return _jspx_imports_packages;
}
public java.util.Set<java.lang.String> getClassImports() {
return _jspx_imports_classes;
}
public javax.el.ExpressionFactory _jsp_getExpressionFactory() {
if (_el_expressionfactory == null) {
synchronized (this) {
if (_el_expressionfactory == null) {
_el_expressionfactory = _jspxFactory.getJspApplicationContext(getServletConfig().getServletContext()).getExpressionFactory();
}
}
}
return _el_expressionfactory;
}
public org.apache.tomcat.InstanceManager _jsp_getInstanceManager() {
if (_jsp_instancemanager == null) {
synchronized (this) {
if (_jsp_instancemanager == null) {
_jsp_instancemanager = org.apache.jasper.runtime.InstanceManagerFactory.getInstanceManager(getServletConfig());
}
}
}
return _jsp_instancemanager;
}
public void _jspInit() {
_005fjspx_005ftagPool_005ftagtest_005fviewIP_005fnobody = org.apache.jasper.runtime.TagHandlerPool.getTagHandlerPool(getServletConfig());
}
public void _jspDestroy() {
_005fjspx_005ftagPool_005ftagtest_005fviewIP_005fnobody.release();
}
public void _jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response)
throws java.io.IOException, javax.servlet.ServletException {
final java.lang.String _jspx_method = request.getMethod();
if (!"GET".equals(_jspx_method) && !"POST".equals(_jspx_method) && !"HEAD".equals(_jspx_method) && !javax.servlet.DispatcherType.ERROR.equals(request.getDispatcherType())) {
response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, "JSPs only permit GET POST or HEAD");
return;
}
final javax.servlet.jsp.PageContext pageContext;
javax.servlet.http.HttpSession session = null;
final javax.servlet.ServletContext application;
final javax.servlet.ServletConfig config;
javax.servlet.jsp.JspWriter out = null;
final java.lang.Object page = this;
javax.servlet.jsp.JspWriter _jspx_out = null;
javax.servlet.jsp.PageContext _jspx_page_context = null;
try {
response.setContentType("text/html;charset=UTF-8");
pageContext = _jspxFactory.getPageContext(this, request, response,
null, true, 8192, true);
_jspx_page_context = pageContext;
application = pageContext.getServletContext();
config = pageContext.getServletConfig();
session = pageContext.getSession();
out = pageContext.getOut();
_jspx_out = out;
out.write("\n");
out.write("\n");
out.write("<!-- 使用taglib指令引用gacl標簽庫憋活,標簽庫的前綴(prefix)可以隨便設置岂津,如這里設置成 prefix=\"xdp\" -->\n");
out.write("\n");
out.write("<html>\n");
out.write(" <head>\n");
out.write("\t <title>Self Define Tag Test</title>\n");
out.write(" </head>\n");
out.write(" <body>\n");
out.write(" <h1>Get IP Address using different ways:</h1>\n");
out.write(" 你的IP地址是(使用java代碼獲取輸出):\n");
out.write(" ");
//在jsp頁面中使用java代碼獲取客戶端IP地址
String ip = request.getRemoteAddr();
out.write(ip);
out.write("\n");
out.write(" <hr/>\n");
out.write(" 你的IP地址是(使用自定義標簽獲取輸出):\n");
out.write(" ");
out.write("\n");
out.write(" ");
if (_jspx_meth_tagtest_005fviewIP_005f0(_jspx_page_context))
return;
out.write("\n");
out.write(" </body>\n");
out.write("</html>\n");
} catch (java.lang.Throwable t) {
if (!(t instanceof javax.servlet.jsp.SkipPageException)){
out = _jspx_out;
if (out != null && out.getBufferSize() != 0)
try {
if (response.isCommitted()) {
out.flush();
} else {
out.clearBuffer();
}
} catch (java.io.IOException e) {}
if (_jspx_page_context != null) _jspx_page_context.handlePageException(t);
else throw new ServletException(t);
}
} finally {
_jspxFactory.releasePageContext(_jspx_page_context);
}
}
private boolean _jspx_meth_tagtest_005fviewIP_005f0(javax.servlet.jsp.PageContext _jspx_page_context)
throws java.lang.Throwable {
javax.servlet.jsp.PageContext pageContext = _jspx_page_context;
javax.servlet.jsp.JspWriter out = _jspx_page_context.getOut();
// tagtest:viewIP
com.test.selfdefine.tag.ShowIpAddressTag _jspx_th_tagtest_005fviewIP_005f0 = (com.test.selfdefine.tag.ShowIpAddressTag) _005fjspx_005ftagPool_005ftagtest_005fviewIP_005fnobody.get(com.test.selfdefine.tag.ShowIpAddressTag.class);
boolean _jspx_th_tagtest_005fviewIP_005f0_reused = false;
try {
_jspx_th_tagtest_005fviewIP_005f0.setPageContext(_jspx_page_context);
_jspx_th_tagtest_005fviewIP_005f0.setParent(null);
int _jspx_eval_tagtest_005fviewIP_005f0 = _jspx_th_tagtest_005fviewIP_005f0.doStartTag();
if (_jspx_th_tagtest_005fviewIP_005f0.doEndTag() == javax.servlet.jsp.tagext.Tag.SKIP_PAGE) {
return true;
}
_005fjspx_005ftagPool_005ftagtest_005fviewIP_005fnobody.reuse(_jspx_th_tagtest_005fviewIP_005f0);
_jspx_th_tagtest_005fviewIP_005f0_reused = true;
} finally {
org.apache.jasper.runtime.JspRuntimeLibrary.releaseTag(_jspx_th_tagtest_005fviewIP_005f0, _jsp_getInstanceManager(), _jspx_th_tagtest_005fviewIP_005f0_reused);
}
return false;
}
}
從這個代碼中可以看出,_jspService
方法逐行處理jsp文件中的代碼悦即。當遇到自定義標簽時吮成,執(zhí)行_jspx_meth_tagtest_005fviewIP_005f0
方法。從這個方法中辜梳,我們就能看到自定義標簽的執(zhí)行流程粱甫。
JSP引擎遇到自定義標簽時,首先創(chuàng)建標簽處理器類的實例對象作瞄,然后按照JSP規(guī)范定義的通信規(guī)則依次調(diào)用它的方法茶宵。
0、實例化自定義標簽處理器類的實例對象宗挥。
javax.servlet.jsp.PageContext pageContext = _jspx_page_context;
javax.servlet.jsp.JspWriter out = _jspx_page_context.getOut();
// tagtest:viewIP
com.test.selfdefine.tag.ShowIpAddressTag _jspx_th_tagtest_005fviewIP_005f0 = (com.test.selfdefine.tag.ShowIpAddressTag) _005fjspx_005ftagPool_005ftagtest_005fviewIP_005fnobody.get(com.test.selfdefine.tag.ShowIpAddressTag.class);
1乌庶、public void setPageContext(PageContext pc), JSP引擎實例化標簽處理器后契耿,將調(diào)用setPageContext方法將JSP頁面的pageContext對象傳遞給標簽處理器瞒大,標簽處理器以后可以通過這個pageContext對象與JSP頁面進行通信。
_jspx_th_tagtest_005fviewIP_005f0.setPageContext(_jspx_page_context);
2搪桂、public void setParent(Tag t)透敌,setPageContext方法執(zhí)行完后,WEB容器接著調(diào)用的setParent方法將當前標簽的父標簽傳遞給當前標簽處理器踢械,如果當前標簽沒有父標簽拙泽,則傳遞給setParent方法的參數(shù)值為null。
_jspx_th_tagtest_005fviewIP_005f0.setParent(null);
3裸燎、public int doStartTag(),調(diào)用了setPageContext方法和setParent方法之后泼疑,WEB容器執(zhí)行到自定義標簽的開始標記時德绿,就會調(diào)用標簽處理器的doStartTag方法。
int _jspx_eval_tagtest_005fviewIP_005f0 = _jspx_th_tagtest_005fviewIP_005f0.doStartTag();
4、public int doEndTag()移稳,WEB容器執(zhí)行完自定義標簽的標簽體后蕴纳,就會接著去執(zhí)行自定義標簽的結(jié)束標記,此時个粱,WEB容器會去調(diào)用標簽處理器的doEndTag方法古毛。
if (_jspx_th_tagtest_005fviewIP_005f0.doEndTag() == javax.servlet.jsp.tagext.Tag.SKIP_PAGE) {
return true;
}
這里,需要注意_jspx_meth_tagtest_005fviewIP_005f0
的執(zhí)行結(jié)果如果為true都许,_jspService
方法就直接返回了稻薇,后續(xù)的jsp代碼不會被處理。這就是自定義標簽中SKIP_PAGE
的實現(xiàn)原理胶征。
5塞椎、public void release(),通常WEB容器執(zhí)行完自定義標簽后睛低,標簽處理器會駐留在內(nèi)存中案狠,為其它請求服務器,直至停止web應用時钱雷,web容器才會調(diào)用release方法骂铁。
3、Java Web自定義標簽的API
3.1 API概括
JspTag接口下罩抗,有兩個子分支拉庵。一個分支是Tag接口,另一個分支是SimpleTag接口澄暮。前者是最初的自定義標簽接口名段,我們稱為傳統(tǒng)的自定義標簽api拌滋,后者是后來為了簡化自定義標簽的開發(fā)推出的新的api豪娜。如下圖。
JspTag接口是所有自定義標簽的父接口梅惯,它是JSP2.0中新定義的一個標記接口馍刮,沒有任何屬性和方法信夫。JspTag接口有Tag和SimpleTag兩個直接子接口,JSP2.0以前的版本中只有Tag接口卡啰,所以把實現(xiàn)Tag接口的自定義標簽也叫做傳統(tǒng)標簽静稻,把實現(xiàn)SimpleTag接口的自定義標簽叫做簡單標簽。
3.2 傳統(tǒng)標簽API
3.2.1 Tag接口
Tag接口是所有傳統(tǒng)標簽的父接口匈辱,其中定義了兩個重要方法(doStartTag振湾、doEndTag)方法和四個常量(EVAL_BODY_INCLUDE、SKIP_BODY亡脸、EVAL_PAGE押搪、SKIP_PAGE)树酪,這兩個方法和四個常量的作用如下:
(1) WEB容器在解釋執(zhí)行JSP頁面的過程中,遇到自定義標簽的開始標記就會去調(diào)用標簽處理器的doStartTag方法大州,doStartTag方法執(zhí)行完后可以向WEB容器返回常量EVAL_BODY_INCLUDE或SKIP_BODY续语。如果doStartTag方法返回EVAL_BODY_INCLUDE,WEB容器就會接著執(zhí)行自定義標簽的標簽體厦画;如果doStartTag方法返回SKIP_BODY疮茄,WEB容器就會忽略自定義標簽的標簽體,直接解釋執(zhí)行自定義標簽的結(jié)束標記根暑。
(2) WEB容器解釋執(zhí)行到自定義標簽的結(jié)束標記時力试,就會調(diào)用標簽處理器的doEndTag方法,doEndTag方法執(zhí)行完后可以向WEB容器返回常量EVAL_PAGE或SKIP_PAGE购裙。如果doEndTag方法返回常量EVAL_PAGE懂版,WEB容器就會接著執(zhí)行JSP頁面中位于結(jié)束標記后面的JSP代碼;如果doEndTag方法返回SKIP_PAGE躏率,WEB容器就會忽略JSP頁面中位于結(jié)束標記后面的所有內(nèi)容躯畴。
從doStartTag和doEndTag方法的作用和返回值的作用可以看出,開發(fā)自定義標簽時可以在doStartTag方法和doEndTag方法體內(nèi)編寫合適的Java程序代碼來實現(xiàn)具體的功能薇芝,通過控制doStartTag方法和doEndTag方法的返回值蓬抄,還可以告訴WEB容器是否執(zhí)行自定義標簽中的標簽體內(nèi)容和JSP頁面中位于自定義標簽的結(jié)束標記后面的內(nèi)容。
3.2.2 IterationTag接口
IterationTag接口繼承了Tag接口夯到,并在Tag接口的基礎上增加了一個doAfterBody方法和一個EVAL_BODY_AGAIN常量嚷缭。實現(xiàn)IterationTag接口的標簽除了可以完成Tag接口所能完成的功能外,還能夠通知WEB容器是否重復執(zhí)行標簽體內(nèi)容耍贾。對于實現(xiàn)了IterationTag接口的自定義標簽阅爽,WEB容器在執(zhí)行完自定義標簽的標簽體后,將調(diào)用標簽處理器的doAfterBody方法荐开,doAfterBody方法可以向WEB容器返回常量EVAL_BODY_AGAIN或SKIP_BODY付翁。如果doAfterBody方法返回EVAL_BODY_AGAIN,WEB容器就會把標簽體內(nèi)容再重復執(zhí)行一次晃听,執(zhí)行完后接著再調(diào)用doAfterBody方法百侧,如此往復,直到doAfterBody方法返回常量SKIP_BODY能扒,WEB容器才會開始處理標簽的結(jié)束標記和調(diào)用doEndTag方法佣渴。
可見,開發(fā)自定義標簽時初斑,可以通過控制doAfterBody方法的返回值來告訴WEB容器是否重復執(zhí)行標簽體內(nèi)容辛润,從而達到循環(huán)處理標簽體內(nèi)容的效果。例如见秤,可以通過一個實現(xiàn)IterationTag接口的標簽來迭代輸出一個集合中的所有元素频蛔,在標簽體部分指定元素的輸出格式灵迫。
在JSP API中也提供了IterationTag接口的默認實現(xiàn)類TagSupport,我們在編寫自定義標簽的標簽處理器類時晦溪,可以繼承和擴展TagSupport類,這相比實現(xiàn)IterationTag接口將簡化開發(fā)工作挣跋。
3.2.3 BodyTag接口
BodyTag接口繼承了IterationTag接口三圆,并在IterationTag接口的基礎上增加了兩個方法(setBodyContent、doInitBody)和一個EVAL_BODY_BUFFERED常量避咆。實現(xiàn)BodyTag接口的標簽除了可以完成IterationTag接口所能完成的功能舟肉,還可以對標簽體內(nèi)容進行修改。對于實現(xiàn)了BodyTag接口的自定義標簽查库,標簽處理器的doStartTag方法不僅可以返回前面講解的常量EVAL_BODY_INCLUDE或SKIP_BODY路媚,還可以返回常量EVAL_BODY_BUFFERED。如果doStartTag方法返回EVAL_BODY_BUFFERED樊销,WEB容器就會創(chuàng)建一個專用于捕獲標簽體運行結(jié)果的BodyContent對象整慎,然后調(diào)用標簽處理器的setBodyContent方法將BodyContent對象的引用傳遞給標簽處理器,WEB容器接著將標簽體的執(zhí)行結(jié)果寫入到BodyContent對象中围苫。在標簽處理器的后續(xù)事件方法中裤园,可以通過先前保存的BodyContent對象的引用來獲取標簽體的執(zhí)行結(jié)果,然后調(diào)用BodyContent對象特有的方法對BodyContent對象中的內(nèi)容(即標簽體的執(zhí)行結(jié)果)進行修改和控制其輸出剂府。
在JSP API中也提供了BodyTag接口的實現(xiàn)類BodyTagSupport拧揽,我們在編寫能夠修改標簽體內(nèi)容的自定義標簽的標簽處理器類時,可以繼承和擴展BodyTagSupport類腺占,這相比實現(xiàn)BodyTag接口將簡化開發(fā)工作淤袜。
3.2.4 傳統(tǒng)標簽接口中的各個方法可以返回的返回值說明
下圖列舉了Tag接口、IterationTag接口和BodyTag接口中的主要方法及它們分別可以返回的返回值的說明衰伯。
3.3 簡單標簽接口API:SimpleTag
SimpleTag接口是JSP2.0中新增的一個標簽接口铡羡。由于傳統(tǒng)標簽使用三個標簽接口來完成不同的功能,顯得過于繁瑣嚎研,不利于標簽技術(shù)的推廣蓖墅,因此,SUN公司為降低標簽技術(shù)的學習難度临扮,在JSP 2.0中定義了一個更為簡單论矾、便于編寫和調(diào)用的SimpleTag接口。
3.3.1 SimpleTag接口簡介
SimpleTag接口與傳統(tǒng)標簽接口最大的區(qū)別在于杆勇,SimpleTag接口只定義了一個用于處理標簽邏輯的doTag方法贪壳,該方法在WEB容器執(zhí)行自定義標簽時調(diào)用,并且只被調(diào)用一次蚜退。那些使用傳統(tǒng)標簽接口所完成的功能闰靴,例如是否執(zhí)行標簽體彪笼、迭代標簽體、對標簽體內(nèi)容進行修改等功能都可以在doTag方法中完成蚂且。在doTag方法中可以拋出javax.servlet.jsp.SkipPageException異常配猫,用于通知WEB容器不再執(zhí)行JSP頁面中位于結(jié)束標記后面的內(nèi)容,這等效于在傳統(tǒng)標簽的doEndTag方法中返回Tag.SKIP_PAGE常量的情況杏死。
除了doTag方法方法泵肄,SimpleTag接口還定義了如下四個方法:
- setJspContext方法
用于把JSP頁面的pageContext對象傳遞給標簽處理器對象 - setParent方法
用于把父標簽處理器對象傳遞給當前標簽處理器對象 - getParent方法
用于獲得當前標簽的父標簽處理器對象 - setJspBody方法
用于把代表標簽體的JspFragment對象傳遞給標簽處理器對象
在JSP API中也提供了SimpleTag接口的默認實現(xiàn)類SimpleTagSupport,我們在編寫簡單標簽時淑翼,可以繼承和擴展SimpleTagSupport類腐巢,這相比實現(xiàn)SimpleTag接口將簡化開發(fā)工作。這是推薦的做法玄括,只有當默認的實現(xiàn)類不滿足需求才考慮根據(jù)具體的邏輯需求覆蓋重寫冯丙。
3.3.2 SimpleTag接口方法的執(zhí)行順序
當web容器開始執(zhí)行標簽時,會調(diào)用如下方法完成標簽的初始化:
- WEB容器調(diào)用標簽處理器對象的setJspContext方法遭京,將代表JSP頁面的pageContext對象傳遞給標簽處理器對象胃惜。
- WEB容器調(diào)用標簽處理器對象的setParent方法,將父標簽處理器對象傳遞給這個標簽處理器對象洁墙。注意蛹疯,只有在標簽存在父標簽的情況下,WEB容器才會調(diào)用這個方法热监。
- 如果調(diào)用標簽時設置了屬性捺弦,容器將調(diào)用每個屬性對應的setter方法把屬性值傳遞給標簽處理器對象。如果標簽的屬性值是EL表達式或腳本表達式孝扛,則WEB容器首先計算表達式的值列吼,然后把值傳遞給標簽處理器對象。
- 如果簡單標簽有標簽體苦始,WEB容器將調(diào)用setJspBody方法把代表標簽體的JspFragment對象傳遞進來寞钥。
- 執(zhí)行標簽時WEB容器調(diào)用標簽處理器的doTag()方法,開發(fā)人員在方法體內(nèi)通過操作JspFragment對象陌选,就可以實現(xiàn)是否執(zhí)行理郑、迭代、修改標簽體的目的咨油。
4您炉、自定義標簽API代碼示例
4.1 傳統(tǒng)標簽API實例代碼
前面入門的例子中,我們是通過直接實現(xiàn)Tag接口來開發(fā)的自定義標簽役电。SUN公司針對tag接口提供了一個默認的實現(xiàn)類TagSupport赚爵,TagSupport類中實現(xiàn)了tag接口的所有方法,因此,這里我們可以編寫一個類繼承TagSupport類來開發(fā)演示傳統(tǒng)標簽API冀膝。
4.1.1 帶標簽體的展示顯示ip標簽
這里唁奢,我們重新實現(xiàn)一遍前面的顯示ip地址標簽。加入標簽體支持窝剖,在標簽體中麻掸,我們允許配置ip地址獲得說明。
首先枯芬,在WEB-INF/selfdefine.tld
中添加標簽定義:
<?xml version="1.0" encoding="utf-8"?>
<taglib xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd"
version="2.1">
<!-- description用來添加對taglib(標簽庫)的描述 -->
<description>Try Selfdefine Tag</description>
<!--taglib(標簽庫)的版本號 -->
<tlib-version>1.0</tlib-version>
<!-- 這里里面不能有空格 -->
<short-name>SelfdedineTagLibrary</short-name>
<!--
為自定義標簽庫設置一個uri论笔,uri以/開頭,/后面的內(nèi)容隨便寫千所,如這里的/tagtest ,
在Jsp頁面中引用標簽庫時蒜埋,需要通過uri找到標簽庫
在Jsp頁面中就要這樣引入標簽庫:<%@taglib uri="/tagtest" prefix="tagtest"%>
-->
<uri>/tagtest</uri>
<!--一個taglib(標簽庫)中包含多個自定義標簽淫痰,每一個自定義標簽使用一個tag標記來描述 -->
<!-- 一個tag標記對應一個自定義標簽 -->
<tag>
<description>用來輸出客戶端的IP地址</description>
<!--
為標簽處理器類配一個標簽名,在Jsp頁面中使用標簽時是通過標簽名來找到要調(diào)用的標簽處理器類的
通過viewIP就能找到對應的me.gacl.web.tag.ViewIPTag類
-->
<name>viewIP</name>
<!-- 標簽對應的處理器類-->
<tag-class>com.test.selfdefine.tag.ShowIpAddressTag</tag-class>
<body-content>empty</body-content>
</tag>
<tag>
<description>用來輸出客戶端的IP地址</description>
<name>viewIPWithBody</name>
<!-- 標簽對應的處理器類-->
<tag-class>com.test.selfdefine.tag.ShowIpAddressWithBodyTag</tag-class>
<body-content>scriptless</body-content>
</tag>
</taglib>
注意這里的body-content
配置了不同的值整份,含義如下:
(1) empty:空標記待错,即起始標記和結(jié)束標記之間沒有內(nèi)容。
<l:hello />
<l:hello ></l:hello >
(2) scriptless:接受文本烈评、EL和JSP動作火俄,但不能含有腳本元素。
<l:hello >This is message body</l:hello>
(3) JSP:體包含JSP元素讲冠,如EL表達式瓜客,標準或定制動作以及腳本元素。
<l:hello >
<%=request.getProtocol()%>
</l:hello>
(4) tagdependent:體中可以包含看似為JSP元素的內(nèi)容竿开,但是容器不對其進行計算(當體中的內(nèi)容與JSP元素產(chǎn)生混淆時采用這個谱仪,較少用)
</l:hello var="p">
${p.name }:${p.age}
</l:hello>
然后,編寫一個繼承TagSupport類的自定義標簽處理類否彩,并按需重寫其中的方法疯攒。
com.test.selfdefine.tag.ShowIpAddressWithBodyTag
:
package com.test.selfdefine.tag;
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;
import javax.servlet.jsp.tagext.TagSupport;
import java.io.IOException;
/**
* Created by chengxia on 2019/10/11.
*/
public class ShowIpAddressWithBodyTag extends TagSupport {
//接收傳遞進來的PageContext對象
private PageContext pageContext;
@Override
public void setPageContext(PageContext pageContext) {
this.pageContext = pageContext;
System.out.println("setPageContext(PageContext pageContext) function executing...");
}
@Override
public int doStartTag() throws JspException {
System.out.println("doStartTag() function executing...");
HttpServletRequest request =(HttpServletRequest) pageContext.getRequest();
JspWriter out = pageContext.getOut();
String ip = request.getRemoteAddr();
try {
//這里輸出的時候會拋出IOException異常
out.write(ip);
} catch (IOException e) {
//捕獲IOException異常后繼續(xù)拋出
throw new RuntimeException(e);
}
return Tag.EVAL_BODY_INCLUDE;
}
}
注意,這里為了標簽體中的內(nèi)容能夠被處理列荔,doStartTag()
方法返回的是Tag.EVAL_BODY_INCLUDE
敬尺。
最后是標簽調(diào)用。
index.jsp
:
<%--
Created by IntelliJ IDEA.
User: chengxia
Date: 2019/10/10
Time: 8:43 AM
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!-- 使用taglib指令引用gacl標簽庫贴浙,標簽庫的前綴(prefix)可以隨便設置砂吞,如這里設置成 prefix="xdp" -->
<%@taglib uri="/tagtest" prefix="tagtest"%>
<html>
<head>
<title>Self Define Tag Test</title>
</head>
<body>
<h1>Get IP Address using different ways:</h1>
你的IP地址是(使用java代碼獲取輸出):
<%
//在jsp頁面中使用java代碼獲取客戶端IP地址
String ip = request.getRemoteAddr();
out.write(ip);
%>
<hr/>
你的IP地址是(使用自定義標簽獲取輸出):
<%--使用自定義標簽viewIP --%>
<tagtest:viewIP/>
<hr/>
你的IP地址是(使用自定義標簽獲取輸出,帶標簽體):
<%--使用自定義標簽viewIP --%>
<tagtest:viewIPWithBody>(Ip Address got by self define tag.)</tagtest:viewIPWithBody>
</body>
</html>
啟動服務器之后悬而,運行效果如下:
如果想讓自定義標簽體中的提示信息出現(xiàn)在ip地址之前呜舒,怎么做呢?只需要將輸出ip的邏輯放到doEngTag方法中就可以。如下:
package com.test.selfdefine.tag;
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;
import javax.servlet.jsp.tagext.TagSupport;
import java.io.IOException;
/**
* Created by chengxia on 2019/10/11.
*/
public class ShowIpAddressWithBodyTag extends TagSupport {
//接收傳遞進來的PageContext對象
private PageContext pageContext;
@Override
public void setPageContext(PageContext pageContext) {
this.pageContext = pageContext;
System.out.println("setPageContext(PageContext pageContext) function executing...");
}
@Override
public int doStartTag() throws JspException {
return Tag.EVAL_BODY_INCLUDE;
}
@Override
public int doEndTag() throws JspException{
System.out.println("doStartTag() function executing...");
HttpServletRequest request =(HttpServletRequest) pageContext.getRequest();
JspWriter out = pageContext.getOut();
String ip = request.getRemoteAddr();
try {
//這里輸出的時候會拋出IOException異常
out.write(ip);
} catch (IOException e) {
//捕獲IOException異常后繼續(xù)拋出
throw new RuntimeException(e);
}
return Tag.EVAL_PAGE;
}
}
運行效果如下:
如果想忽略這個標簽體袭蝗,只需改變doStartTag
方法的返回值唤殴。如下:
com.test.selfdefine.tag.ShowIpAddressWithBodyTag
:
package com.test.selfdefine.tag;
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;
import javax.servlet.jsp.tagext.TagSupport;
import java.io.IOException;
/**
* Created by chengxia on 2019/10/11.
*/
public class ShowIpAddressWithBodyTag extends TagSupport {
//接收傳遞進來的PageContext對象
private PageContext pageContext;
@Override
public void setPageContext(PageContext pageContext) {
this.pageContext = pageContext;
System.out.println("setPageContext(PageContext pageContext) function executing...");
}
@Override
public int doStartTag() throws JspException {
return Tag.SKIP_BODY;
}
@Override
public int doEndTag() throws JspException{
System.out.println("doStartTag() function executing...");
HttpServletRequest request =(HttpServletRequest) pageContext.getRequest();
JspWriter out = pageContext.getOut();
String ip = request.getRemoteAddr();
try {
//這里輸出的時候會拋出IOException異常
out.write(ip);
} catch (IOException e) {
//捕獲IOException異常后繼續(xù)拋出
throw new RuntimeException(e);
}
return Tag.EVAL_PAGE;
}
}
效果如下:
如果想讓標簽之后的頁面元素都不被處理,只需修改doEndTag的返回值到腥。如下朵逝。
com.test.selfdefine.tag.ShowIpAddressWithBodyTag
:
package com.test.selfdefine.tag;
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;
import javax.servlet.jsp.tagext.TagSupport;
import java.io.IOException;
/**
* Created by chengxia on 2019/10/11.
*/
public class ShowIpAddressWithBodyTag extends TagSupport {
//接收傳遞進來的PageContext對象
private PageContext pageContext;
@Override
public void setPageContext(PageContext pageContext) {
this.pageContext = pageContext;
System.out.println("setPageContext(PageContext pageContext) function executing...");
}
@Override
public int doStartTag() throws JspException {
return Tag.SKIP_BODY;
}
@Override
public int doEndTag() throws JspException{
System.out.println("doStartTag() function executing...");
HttpServletRequest request =(HttpServletRequest) pageContext.getRequest();
JspWriter out = pageContext.getOut();
String ip = request.getRemoteAddr();
try {
//這里輸出的時候會拋出IOException異常
out.write(ip);
} catch (IOException e) {
//捕獲IOException異常后繼續(xù)拋出
throw new RuntimeException(e);
}
return Tag.SKIP_PAGE;
}
}
效果如下:
可以看到盡管顯式?jīng)]有區(qū)別,但是乡范,html文件最后的閉合標簽都是缺失的配名。原因就是自定義標簽之后的內(nèi)容,都被doEndTag()
函數(shù)返回Tag.SKIP_PAGE
之后略過不處理了晋辆。
4.1.2 控制jsp頁面內(nèi)容重復執(zhí)行
編寫一個類實現(xiàn)Iterationtag接口(這里采用的是繼承現(xiàn)有的實現(xiàn)了TagSupport)渠脉,控制doAfterBody()方法的返回值,如果這個方法返回EVAL_BODY_AGAIN瓶佳, 則web服務器又執(zhí)行一次標簽體芋膘,依次類推,一直執(zhí)行到doAfterBody方法返回SKIP_BODY霸饲,則標簽體才不會重復執(zhí)行为朋。
首先定義一個自定義標簽處理類。
com.test.selfdefine.tag.RepeatBodyTag
:
package com.test.selfdefine.tag;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.IterationTag;
import javax.servlet.jsp.tagext.Tag;
import javax.servlet.jsp.tagext.TagSupport;
/**
* Created by chengxia on 2019/10/11.
*/
public class RepeatBodyTag extends TagSupport {
int repeatTimes = 3;
/**
* 返回Tag.EVAL_BODY_INCLUDE厚脉,以至于能夠處理標簽體
* */
@Override
public int doStartTag() throws JspException {
return Tag.EVAL_BODY_INCLUDE;
}
/* 控制doAfterBody()方法的返回值习寸,
* 如果這個方法返回EVAL_BODY_AGAIN, 則web服務器又執(zhí)行一次標簽體傻工,
* 依次類推霞溪,一直執(zhí)行到doAfterBody方法返回SKIP_BODY,則標簽體才不會重復執(zhí)行精钮。
* @see javax.servlet.jsp.tagext.TagSupport#doAfterBody()
*/
@Override
public int doAfterBody() throws JspException {
repeatTimes--;
if (repeatTimes > 0) {
return IterationTag.EVAL_BODY_AGAIN;
} else {
return IterationTag.SKIP_BODY;
}
}
}
在自定義標簽配置文件中添加條目:
WEB-INF/selfdefine.tld
:
<?xml version="1.0" encoding="utf-8"?>
<taglib xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd"
version="2.1">
<!-- description用來添加對taglib(標簽庫)的描述 -->
<description>Try Selfdefine Tag</description>
<!--taglib(標簽庫)的版本號 -->
<tlib-version>1.0</tlib-version>
<!-- 這里里面不能有空格 -->
<short-name>SelfdedineTagLibrary</short-name>
<!--
為自定義標簽庫設置一個uri威鹿,uri以/開頭,/后面的內(nèi)容隨便寫轨香,如這里的/tagtest 忽你,
在Jsp頁面中引用標簽庫時,需要通過uri找到標簽庫
在Jsp頁面中就要這樣引入標簽庫:<%@taglib uri="/tagtest" prefix="tagtest"%>
-->
<uri>/tagtest</uri>
<!--一個taglib(標簽庫)中包含多個自定義標簽臂容,每一個自定義標簽使用一個tag標記來描述 -->
<!-- 一個tag標記對應一個自定義標簽 -->
<tag>
<description>用來輸出客戶端的IP地址</description>
<!--
為標簽處理器類配一個標簽名科雳,在Jsp頁面中使用標簽時是通過標簽名來找到要調(diào)用的標簽處理器類的
通過viewIP就能找到對應的me.gacl.web.tag.ViewIPTag類
-->
<name>viewIP</name>
<!-- 標簽對應的處理器類-->
<tag-class>com.test.selfdefine.tag.ShowIpAddressTag</tag-class>
<body-content>empty</body-content>
</tag>
<tag>
<description>用來輸出客戶端的IP地址</description>
<name>viewIPWithBody</name>
<!-- 標簽對應的處理器類-->
<tag-class>com.test.selfdefine.tag.ShowIpAddressWithBodyTag</tag-class>
<body-content>scriptless</body-content>
</tag>
<tag>
<description>用來輸出客戶端的IP地址</description>
<name>RepeatBodyTag</name>
<!-- 標簽對應的處理器類-->
<tag-class>com.test.selfdefine.tag.RepeatBodyTag</tag-class>
<body-content>JSP</body-content>
</tag>
</taglib>
寫一個jsp文件,測試剛剛創(chuàng)建的自定義標簽:
test.jsp
:
<%--
Created by IntelliJ IDEA.
User: chengxia
Date: 2019/10/10
Time: 8:43 AM
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!-- 使用taglib指令引用gacl標簽庫脓杉,標簽庫的前綴(prefix)可以隨便設置糟秘,如這里設置成 prefix="xdp" -->
<%@taglib uri="/tagtest" prefix="tagtest"%>
<html>
<head>
<title>Self Define Tag Test</title>
</head>
<body>
<tagtest:RepeatBodyTag>
<h2>This is content for repeat tag.</h2>
</tagtest:RepeatBodyTag>
</body>
</html>
啟動服務器,第一次訪問http://localhost:8080/test.jsp
:
第二次訪問:
至于原因球散,從前面的自定義標簽執(zhí)行流程不難理解尿赚。自定義標簽處理類,只實例化一次,第一次之后凌净,x的值就變成0了悲龟。所以,第一次之后冰寻,后面只會對該自定義標簽的標簽體渲染一次须教。
4.1.3 修改jsp頁面內(nèi)容輸出
編寫一個類實現(xiàn)BodyTag接口,控制doStartTag()方法返回EVAL_BODY_BUFFERED斩芭,則web服務器會創(chuàng)建BodyContent對象捕獲標簽體轻腺,然后在doEndTag()方法體內(nèi)陕凹,得到代表標簽體的bodyContent對象工秩,從而就可以對標簽體進行修改操作捏鱼。
SUN公司針對BodyTag接口提供了一個默認的實現(xiàn)類BodyTagSupport施无,BodyTagSupport類中實現(xiàn)了BodyTag接口的所有方法,因此我們可以編寫一個類繼承BodyTagSupport類啤誊,然后再根據(jù)需要重寫doStartTag方法和doEndTag()方法豪嚎。
同樣舷礼,也是细卧,先定義一個自定義標簽處理類。作為演示筒占,其作用是將標簽體中的內(nèi)容全部轉(zhuǎn)化為大寫贪庙。
com.test.selfdefine.tag.ModBodyContentTag
:
package com.test.selfdefine.tag;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.*;
import java.io.IOException;
/**
* Created by chengxia on 2019/10/11.
*/
public class ModBodyContentTag extends BodyTagSupport {
/**
* 控制doStartTag()方法返回EVAL_BODY_BUFFERED
* */
@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();
//修改標簽體里面的內(nèi)容,將標簽體的內(nèi)容轉(zhuǎn)換成大寫
String result = content.toUpperCase();
try {
//輸出修改后的內(nèi)容
this.pageContext.getOut().write(result);
} catch (IOException e) {
throw new RuntimeException(e);
}
return Tag.EVAL_PAGE;
}
}
在自定義標簽配置文件中翰苫,添加自定義標簽配置止邮。
WEB-INF/selfdefine.tld
:
<?xml version="1.0" encoding="utf-8"?>
<taglib xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd"
version="2.1">
<!-- description用來添加對taglib(標簽庫)的描述 -->
<description>Try Selfdefine Tag</description>
<!--taglib(標簽庫)的版本號 -->
<tlib-version>1.0</tlib-version>
<!-- 這里里面不能有空格 -->
<short-name>SelfdedineTagLibrary</short-name>
<!--
為自定義標簽庫設置一個uri,uri以/開頭奏窑,/后面的內(nèi)容隨便寫导披,如這里的/tagtest ,
在Jsp頁面中引用標簽庫時埃唯,需要通過uri找到標簽庫
在Jsp頁面中就要這樣引入標簽庫:<%@taglib uri="/tagtest" prefix="tagtest"%>
-->
<uri>/tagtest</uri>
<!--一個taglib(標簽庫)中包含多個自定義標簽撩匕,每一個自定義標簽使用一個tag標記來描述 -->
<!-- 一個tag標記對應一個自定義標簽 -->
<tag>
<description>用來輸出客戶端的IP地址</description>
<!--
為標簽處理器類配一個標簽名,在Jsp頁面中使用標簽時是通過標簽名來找到要調(diào)用的標簽處理器類的
通過viewIP就能找到對應的me.gacl.web.tag.ViewIPTag類
-->
<name>viewIP</name>
<!-- 標簽對應的處理器類-->
<tag-class>com.test.selfdefine.tag.ShowIpAddressTag</tag-class>
<body-content>empty</body-content>
</tag>
<tag>
<description>用來輸出客戶端的IP地址</description>
<name>viewIPWithBody</name>
<!-- 標簽對應的處理器類-->
<tag-class>com.test.selfdefine.tag.ShowIpAddressWithBodyTag</tag-class>
<body-content>scriptless</body-content>
</tag>
<tag>
<description>用來輸出客戶端的IP地址</description>
<name>RepeatBodyTag</name>
<!-- 標簽對應的處理器類-->
<tag-class>com.test.selfdefine.tag.RepeatBodyTag</tag-class>
<body-content>JSP</body-content>
</tag>
<tag>
<description>用來輸出客戶端的IP地址</description>
<name>ModBodyContentTag</name>
<!-- 標簽對應的處理器類-->
<tag-class>com.test.selfdefine.tag.ModBodyContentTag</tag-class>
<body-content>JSP</body-content>
</tag>
</taglib>
最后墨叛,寫一個測試頁面來測試這個自定義標簽止毕。
test.jsp
:
<%--
Created by IntelliJ IDEA.
User: chengxia
Date: 2019/10/10
Time: 8:43 AM
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!-- 使用taglib指令引用gacl標簽庫,標簽庫的前綴(prefix)可以隨便設置漠趁,如這里設置成 prefix="xdp" -->
<%@taglib uri="/tagtest" prefix="tagtest"%>
<html>
<head>
<title>Self Define Tag Test</title>
</head>
<body>
<tagtest:RepeatBodyTag>
<h2>This is content for repeat tag.</h2>
</tagtest:RepeatBodyTag>
<hr/>
<tagtest:ModBodyContentTag>
This is content to be modified.
</tagtest:ModBodyContentTag>
</body>
</html>
重啟tomcat扁凛,訪問http://localhost:8080/test.jsp
,效果如下闯传。
4.2 簡單標簽API示例代碼
SUN公司針對SimpleTag接口提供了一個默認的實現(xiàn)類SimpleTagSupport谨朝,SimpleTagSupport類中實現(xiàn)了SimpleTag接口的所有方法,因此我們可以編寫一個類繼承SimpleTagSupport類,然后根據(jù)業(yè)務需要再重寫doTag方法字币。
4.2.1 控制jsp中自定義標簽體中內(nèi)容是否執(zhí)行
這里则披,我們編寫一個類繼承SimpleTagSupport,然后再重寫doTag方法纬朝,在doTag方法里面不調(diào)用jspFrament.invoke方法即可收叶。
首先,我們新建一個標簽處理類com.test.selfdefine.tag.SimpleTagTest1
共苛。
package com.test.selfdefine.tag;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.JspFragment;
import javax.servlet.jsp.tagext.SimpleTagSupport;
import java.io.IOException;
/**
* Created by chengxia on 2019/10/23.
*/
public class SimpleTagTest1 extends SimpleTagSupport {
/* 簡單標簽使用這個方法就可以完成所有的業(yè)務邏輯
* @see javax.servlet.jsp.tagext.SimpleTagSupport#doTag()
* 重寫doTag方法判没,控制標簽體是否執(zhí)行
*/
@Override
public void doTag() throws JspException, IOException {
//得到代表jsp標簽體的JspFragment
JspFragment jspFragment = this.getJspBody();
//得到jsp頁面的的PageContext對象
//PageContext pageContext = (PageContext) jspFragment.getJspContext();
//調(diào)用JspWriter將標簽體的內(nèi)容輸出到瀏覽器
//jspFragment.invoke(pageContext.getOut());
//這兩行代碼和下面的一行效果一樣
//將標簽體的內(nèi)容輸出到瀏覽器,如果注掉這一行隅茎,就不會顯示標簽體
jspFragment.invoke(null);
}
}
然后澄峰,我們創(chuàng)建一個標簽配置文件WEB-INF/simpletag.tld
。
<?xml version="1.0" encoding="ISO-8859-1"?>
<taglib xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd"
version="2.1">
<!-- description用來添加對taglib(標簽庫)的描述 -->
<description>SimpleTag Test</description>
<tlib-version>1.0</tlib-version>
<short-name>SimpleTagLibraryTest</short-name>
<!--
為自定義標簽庫設置一個uri辟犀,uri以/開頭俏竞,/后面的內(nèi)容隨便寫,如這里的/simpletag 堂竟,
在Jsp頁面中引用標簽庫時魂毁,需要通過uri找到標簽庫
在Jsp頁面中就要這樣引入標簽庫:<%@taglib uri="/simpletag" prefix="simptag"%>
-->
<uri>/simpletag</uri>
<!-- Invoke 'Generate' action to add tags or functions -->
<!--一個taglib(標簽庫)中包含多個自定義標簽,每一個自定義標簽使用一個tag標記來描述 -->
<!-- 一個tag標記對應一個自定義標簽 -->
<tag>
<description>SimpleTagTest1</description>
<!--
為標簽處理器類配一個標簽名出嘹,在Jsp頁面中使用標簽時是通過標簽名來找到要調(diào)用的標簽處理器類的
通過SimpleTag1就能找到對應的com.test.selfdefine.tag.SimpleTagTest1類
-->
<name>SimpleTag1</name>
<!-- 標簽對應的處理器類-->
<tag-class>com.test.selfdefine.tag.SimpleTagTest1</tag-class>
<!--
tld文件中有四種標簽體類型 :empty JSP scriptless tagdepentend
在簡單標簽(SampleTag)中標簽體body-content的值只允許是empty和scriptless席楚,不允許設置成JSP,如果設置成JSP就會出現(xiàn)異常
在傳統(tǒng)標簽中標簽體body-content的值只允許是empty和JSP
如果標簽體body-content的值設置成tagdepentend,那么就表示標簽體里面的內(nèi)容是給標簽處理器類使用的税稼,
例如:開發(fā)一個查詢用戶的sql標簽烦秩,此時標簽體重的SQL語句就是給SQL標簽的標簽處理器來使用的
<selftag:sql>SELECT * FROM USER</selftag:sql>
在這種情況下,sql標簽的<body-content>就要設置成tagdepentend郎仆,tagdepentend用得比較少只祠,了解一下即可
-->
<body-content>scriptless</body-content>
</tag>
</taglib>
最后,寫一個jsp文件SimpleTag.jsp
來測試該自定義標簽扰肌。
<%--
Created by IntelliJ IDEA.
User: chengxia
Date: 2019/10/10
Time: 8:43 AM
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!-- 使用taglib指令引用simptag標簽庫抛寝,標簽庫的前綴(prefix)可以隨便設置,如這里設置成 prefix="simptag" -->
<%@ taglib prefix="simptag" uri="/simpletag" %>
<html>
<head>
<title>Self Define Tag Test</title>
</head>
<body>
<h1>Ha Ha</h1>
<hr/>
<simptag:SimpleTag1>
<h2>This is content for a simple tag.</h2>
</simptag:SimpleTag1>
</body>
</html>
這樣狡耻,啟動服務器墩剖,訪問http://localhost:8080/SimpleTag.jsp
,效果如下夷狰。
如果注掉標簽處理類中的jspFragment.invoke(null);
岭皂,再重啟服務器,訪問http://localhost:8080/SimpleTag.jsp
沼头,效果如下爷绘。
這樣书劝,就是實現(xiàn)了通過SimpleTag接口來控制標簽體是否輸出到頁面。
4.2.2 控制自定義標簽的標簽體內(nèi)容重復執(zhí)行
這里土至,同樣也是編寫一個類繼承SimpleTagSupport购对,然后再重寫doTag方法,在doTag方法里面重復調(diào)用jspFrament.invoke方法即可陶因。
新建一個標簽處理類com.test.selfdefine.tag.SimpleTagRepeat
骡苞。
package com.test.selfdefine.tag;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.JspFragment;
import javax.servlet.jsp.tagext.SimpleTagSupport;
import java.io.IOException;
/**
* Created by chengxia on 2019/10/23.
*/
public class SimpleTagRepeat extends SimpleTagSupport {
/* 簡單標簽使用這個方法就可以完成所有的業(yè)務邏輯
* @see javax.servlet.jsp.tagext.SimpleTagSupport#doTag()
* 重寫doTag方法,控制標簽體是否重復執(zhí)行楷扬,這里重復輸出3次標簽體解幽。
*/
@Override
public void doTag() throws JspException, IOException {
// 得到代表jsp標簽體的JspFragment
JspFragment jspFragment = this.getJspBody();
for (int i = 0; i < 3; i++) {
// 將標簽體的內(nèi)容輸出到瀏覽器
jspFragment.invoke(null);
}
}
}
在標簽配置文件WEB-INF/simpletag.tld
中添加這一個自定義標簽配置。
<?xml version="1.0" encoding="ISO-8859-1"?>
<taglib xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd"
version="2.1">
<!-- description用來添加對taglib(標簽庫)的描述 -->
<description>SimpleTag Test</description>
<tlib-version>1.0</tlib-version>
<short-name>SimpleTagLibraryTest</short-name>
<!--
為自定義標簽庫設置一個uri烘苹,uri以/開頭躲株,/后面的內(nèi)容隨便寫,如這里的/simpletag 镣衡,
在Jsp頁面中引用標簽庫時霜定,需要通過uri找到標簽庫
在Jsp頁面中就要這樣引入標簽庫:<%@taglib uri="/simpletag" prefix="simptag"%>
-->
<uri>/simpletag</uri>
<!-- Invoke 'Generate' action to add tags or functions -->
<!--一個taglib(標簽庫)中包含多個自定義標簽,每一個自定義標簽使用一個tag標記來描述 -->
<!-- 一個tag標記對應一個自定義標簽 -->
<tag>
<description>SimpleTagTest1</description>
<!--
為標簽處理器類配一個標簽名廊鸥,在Jsp頁面中使用標簽時是通過標簽名來找到要調(diào)用的標簽處理器類的
通過SimpleTag1就能找到對應的com.test.selfdefine.tag.SimpleTagTest1類
-->
<name>SimpleTag1</name>
<!-- 標簽對應的處理器類-->
<tag-class>com.test.selfdefine.tag.SimpleTagTest1</tag-class>
<!--
tld文件中有四種標簽體類型 :empty JSP scriptless tagdepentend
在簡單標簽(SampleTag)中標簽體body-content的值只允許是empty和scriptless望浩,不允許設置成JSP,如果設置成JSP就會出現(xiàn)異常
在傳統(tǒng)標簽中標簽體body-content的值只允許是empty和JSP
如果標簽體body-content的值設置成tagdepentend,那么就表示標簽體里面的內(nèi)容是給標簽處理器類使用的惰说,
例如:開發(fā)一個查詢用戶的sql標簽曾雕,此時標簽體重的SQL語句就是給SQL標簽的標簽處理器來使用的
<selftag:sql>SELECT * FROM USER</selftag:sql>
在這種情況下,sql標簽的<body-content>就要設置成tagdepentend助被,tagdepentend用得比較少,了解一下即可
-->
<body-content>scriptless</body-content>
</tag>
<tag>
<!-- 標簽名 -->
<name>SimpleTagRepeat</name>
<!-- 標簽處理器類-->
<tag-class>com.test.selfdefine.tag.SimpleTagRepeat</tag-class>
<!-- 標簽體允許的內(nèi)容 切诀,scriptless表示標簽體的內(nèi)容不允許是java腳本代碼-->
<body-content>scriptless</body-content>
</tag>
</taglib>
寫一個測試頁面SimpleTag.jsp
揩环,引用這一個自定義標簽。
<%--
Created by IntelliJ IDEA.
User: chengxia
Date: 2019/10/10
Time: 8:43 AM
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!-- 使用taglib指令引用simptag標簽庫幅虑,標簽庫的前綴(prefix)可以隨便設置丰滑,如這里設置成 prefix="simptag" -->
<%@ taglib prefix="simptag" uri="/simpletag" %>
<html>
<head>
<title>Self Define Tag Test</title>
</head>
<body>
<h1>Ha Ha</h1>
<hr/>
<simptag:SimpleTag1>
<h2>This is content for a simple tag.</h2>
</simptag:SimpleTag1>
<hr/>
<simptag:SimpleTagRepeat>
<h2>Content for simple tag repeat.</h2>
</simptag:SimpleTagRepeat>
</body>
</html>
啟動服務器之后,訪問http://localhost:8080/SimpleTag.jsp
倒庵,效果如下褒墨。
4.2.3 修改標簽體的輸出內(nèi)容
編寫一個類繼承SimpleTagSupport,然后再重寫doTag方法擎宝,在doTag方法調(diào)用jspFrament.invoke方法時郁妈,讓執(zhí)行結(jié)果寫一個自定義的緩沖中即可,然后開發(fā)人員可以取出緩沖的數(shù)據(jù)修改輸出绍申。
標簽處理類com.test.selfdefine.tag.SimpleTagModify
:
package com.test.selfdefine.tag;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.JspFragment;
import javax.servlet.jsp.tagext.SimpleTagSupport;
import java.io.IOException;
import java.io.StringWriter;
/**
* Created by chengxia on 2019/10/23.
*/
public class SimpleTagModify extends SimpleTagSupport {
/* 簡單標簽使用這個方法就可以完成所有的業(yè)務邏輯
* @see javax.servlet.jsp.tagext.SimpleTagSupport#doTag()
* 重寫doTag方法噩咪,控制標簽體是否重復執(zhí)行顾彰,這里將標簽體的內(nèi)容轉(zhuǎn)化為大寫之后輸出。
*/
@Override
public void doTag() throws JspException, IOException {
// 得到代表jsp標簽體的JspFragment
JspFragment jspFragment = this.getJspBody();
StringWriter sw = new StringWriter();
//將標簽體的內(nèi)容寫入到sw流中
jspFragment.invoke(sw);
//獲取sw流緩沖區(qū)的內(nèi)容
String content = sw.getBuffer().toString();
content = content.toUpperCase();
PageContext pageContext = (PageContext) this.getJspContext();
//將修改后的content輸出到瀏覽器中
pageContext.getOut().write(content);
}
}
在自定義標簽配置文件WEB-INF/simpletag.tld
中添加該自定義標簽的配置胃碾。
<?xml version="1.0" encoding="ISO-8859-1"?>
<taglib xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd"
version="2.1">
<!-- description用來添加對taglib(標簽庫)的描述 -->
<description>SimpleTag Test</description>
<tlib-version>1.0</tlib-version>
<short-name>SimpleTagLibraryTest</short-name>
<!--
為自定義標簽庫設置一個uri涨享,uri以/開頭,/后面的內(nèi)容隨便寫仆百,如這里的/simpletag 厕隧,
在Jsp頁面中引用標簽庫時,需要通過uri找到標簽庫
在Jsp頁面中就要這樣引入標簽庫:<%@taglib uri="/simpletag" prefix="simptag"%>
-->
<uri>/simpletag</uri>
<!-- Invoke 'Generate' action to add tags or functions -->
<!--一個taglib(標簽庫)中包含多個自定義標簽俄周,每一個自定義標簽使用一個tag標記來描述 -->
<!-- 一個tag標記對應一個自定義標簽 -->
<tag>
<description>SimpleTagTest1</description>
<!--
為標簽處理器類配一個標簽名吁讨,在Jsp頁面中使用標簽時是通過標簽名來找到要調(diào)用的標簽處理器類的
通過SimpleTag1就能找到對應的com.test.selfdefine.tag.SimpleTagTest1類
-->
<name>SimpleTag1</name>
<!-- 標簽對應的處理器類-->
<tag-class>com.test.selfdefine.tag.SimpleTagTest1</tag-class>
<!--
tld文件中有四種標簽體類型 :empty JSP scriptless tagdepentend
在簡單標簽(SampleTag)中標簽體body-content的值只允許是empty和scriptless,不允許設置成JSP,如果設置成JSP就會出現(xiàn)異常
在傳統(tǒng)標簽中標簽體body-content的值只允許是empty和JSP
如果標簽體body-content的值設置成tagdepentend栈源,那么就表示標簽體里面的內(nèi)容是給標簽處理器類使用的挡爵,
例如:開發(fā)一個查詢用戶的sql標簽,此時標簽體重的SQL語句就是給SQL標簽的標簽處理器來使用的
<selftag:sql>SELECT * FROM USER</selftag:sql>
在這種情況下甚垦,sql標簽的<body-content>就要設置成tagdepentend茶鹃,tagdepentend用得比較少,了解一下即可
-->
<body-content>scriptless</body-content>
</tag>
<tag>
<!-- 標簽名 -->
<name>SimpleTagRepeat</name>
<!-- 標簽處理器類-->
<tag-class>com.test.selfdefine.tag.SimpleTagRepeat</tag-class>
<!-- 標簽體允許的內(nèi)容 艰亮,scriptless表示標簽體的內(nèi)容不允許是java腳本代碼-->
<body-content>scriptless</body-content>
</tag>
<tag>
<!-- 標簽名 -->
<name>SimpleTagModify</name>
<!-- 標簽處理器類-->
<tag-class>com.test.selfdefine.tag.SimpleTagModify</tag-class>
<!-- 標簽體允許的內(nèi)容 闭翩,scriptless表示標簽體的內(nèi)容不允許是java腳本代碼-->
<body-content>scriptless</body-content>
</tag>
</taglib>
寫一個頁面SimpleTag.jsp
,引用該自定義標簽迄埃。
<%--
Created by IntelliJ IDEA.
User: chengxia
Date: 2019/10/10
Time: 8:43 AM
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!-- 使用taglib指令引用simptag標簽庫疗韵,標簽庫的前綴(prefix)可以隨便設置,如這里設置成 prefix="simptag" -->
<%@ taglib prefix="simptag" uri="/simpletag" %>
<html>
<head>
<title>Self Define Tag Test</title>
</head>
<body>
<h1>Ha Ha</h1>
<hr/>
<simptag:SimpleTag1>
<h2>This is content for a simple tag.</h2>
</simptag:SimpleTag1>
<hr/>
<simptag:SimpleTagModify>
<h2>Content for simple tag repeat.</h2>
</simptag:SimpleTagModify>
</body>
</html>
啟動服務器之后侄非,訪問http://localhost:8080/SimpleTag.jsp
蕉汪,效果如下。
4.2.4 控制后續(xù)頁面元素是否執(zhí)行
如果doTag方法拋出SkipPageException異常逞怨,后續(xù)jsp頁面的內(nèi)容將不再執(zhí)行者疤。下面還是通過繼承SimpleTagSupport類來演示。
首先叠赦,寫一個自定義標簽處理類com.test.selfdefine.tag.SimpleTagSkipRest
驹马。
package com.test.selfdefine.tag;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.SkipPageException;
import javax.servlet.jsp.tagext.JspFragment;
import javax.servlet.jsp.tagext.SimpleTagSupport;
import java.io.IOException;
import java.io.StringWriter;
/**
* Created by chengxia on 2019/10/23.
*/
public class SimpleTagSkipRest extends SimpleTagSupport {
/* 簡單標簽使用這個方法就可以完成所有的業(yè)務邏輯
* @see javax.servlet.jsp.tagext.SimpleTagSupport#doTag()
* 重寫doTag方法,這里拋出SkipPageException異常除秀,讓后續(xù)的頁面不再執(zhí)行糯累。
*/
@Override
public void doTag() throws JspException, IOException {
//這里拋出SkipPageException異常,讓后續(xù)的jsp頁面不執(zhí)行
throw new SkipPageException();
}
}
在自定義標簽配置文件WEB-INF/simpletag.tld
中册踩,添加這個標簽的定義泳姐。
<?xml version="1.0" encoding="utf-8"?>
<taglib xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd"
version="2.1">
<!-- description用來添加對taglib(標簽庫)的描述 -->
<description>SimpleTag Test</description>
<tlib-version>1.0</tlib-version>
<short-name>SimpleTagLibraryTest</short-name>
<!--
為自定義標簽庫設置一個uri,uri以/開頭暂吉,/后面的內(nèi)容隨便寫仗岸,如這里的/simpletag 允耿,
在Jsp頁面中引用標簽庫時,需要通過uri找到標簽庫
在Jsp頁面中就要這樣引入標簽庫:<%@taglib uri="/simpletag" prefix="simptag"%>
-->
<uri>/simpletag</uri>
<!-- Invoke 'Generate' action to add tags or functions -->
<!--一個taglib(標簽庫)中包含多個自定義標簽扒怖,每一個自定義標簽使用一個tag標記來描述 -->
<!-- 一個tag標記對應一個自定義標簽 -->
<tag>
<description>SimpleTagTest1</description>
<!--
為標簽處理器類配一個標簽名较锡,在Jsp頁面中使用標簽時是通過標簽名來找到要調(diào)用的標簽處理器類的
通過SimpleTag1就能找到對應的com.test.selfdefine.tag.SimpleTagTest1類
-->
<name>SimpleTag1</name>
<!-- 標簽對應的處理器類-->
<tag-class>com.test.selfdefine.tag.SimpleTagTest1</tag-class>
<!--
tld文件中有四種標簽體類型 :empty JSP scriptless tagdepentend
在簡單標簽(SampleTag)中標簽體body-content的值只允許是empty和scriptless,不允許設置成JSP,如果設置成JSP就會出現(xiàn)異常
在傳統(tǒng)標簽中標簽體body-content的值只允許是empty和JSP
如果標簽體body-content的值設置成tagdepentend盗痒,那么就表示標簽體里面的內(nèi)容是給標簽處理器類使用的蚂蕴,
例如:開發(fā)一個查詢用戶的sql標簽,此時標簽體重的SQL語句就是給SQL標簽的標簽處理器來使用的
<selftag:sql>SELECT * FROM USER</selftag:sql>
在這種情況下俯邓,sql標簽的<body-content>就要設置成tagdepentend骡楼,tagdepentend用得比較少,了解一下即可
-->
<body-content>scriptless</body-content>
</tag>
<tag>
<!-- 標簽名 -->
<name>SimpleTagRepeat</name>
<!-- 標簽處理器類-->
<tag-class>com.test.selfdefine.tag.SimpleTagRepeat</tag-class>
<!-- 標簽體允許的內(nèi)容 稽鞭,scriptless表示標簽體的內(nèi)容不允許是java腳本代碼-->
<body-content>scriptless</body-content>
</tag>
<tag>
<!-- 標簽名 -->
<name>SimpleTagModify</name>
<!-- 標簽處理器類-->
<tag-class>com.test.selfdefine.tag.SimpleTagModify</tag-class>
<!-- 標簽體允許的內(nèi)容 鸟整,scriptless表示標簽體的內(nèi)容不允許是java腳本代碼-->
<body-content>scriptless</body-content>
</tag>
<tag>
<!-- 標簽名 -->
<name>SimpleTagSkipRest</name>
<!-- 標簽處理器類-->
<tag-class>com.test.selfdefine.tag.SimpleTagSkipRest</tag-class>
<!-- 標簽體允許的內(nèi)容 ,scriptless表示標簽體的內(nèi)容不允許是java腳本代碼-->
<body-content>scriptless</body-content>
</tag>
</taglib>
在jsp頁面SimpleTag.jsp
中朦蕴,添加這個自定義標簽篮条。
<%--
Created by IntelliJ IDEA.
User: chengxia
Date: 2019/10/10
Time: 8:43 AM
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!-- 使用taglib指令引用simptag標簽庫,標簽庫的前綴(prefix)可以隨便設置吩抓,如這里設置成 prefix="simptag" -->
<%@ taglib prefix="simptag" uri="/simpletag" %>
<html>
<head>
<title>Self Define Tag Test</title>
</head>
<body>
<h1>Ha Ha</h1>
<hr/>
<simptag:SimpleTag1>
<h2>This is content for a simple tag.</h2>
</simptag:SimpleTag1>
<hr/>
<simptag:SimpleTagSkipRest/>
<hr/>
<simptag:SimpleTagModify>
<h2>Content for simple tag repeat.</h2>
</simptag:SimpleTagModify>
</body>
</html>
啟動tomcat服務器之后涉茧,訪問http://localhost:8080/SimpleTag.jsp
,效果如下:
這里可以看出疹娶,SimpleTagSkipRest
后面的內(nèi)容都沒有被執(zhí)行伴栓。
4.3 開發(fā)帶屬性的自定義標簽
這里通過簡單標簽接口來演示如何開發(fā)帶屬性的自定義標簽。
4.3.1 JspFragment類介紹
javax.servlet.jsp.tagext.JspFragment
類是在JSP2.0中定義的雨饺,它的實例對象代表JSP頁面中的一段符合JSP語法規(guī)范的JSP片段钳垮,這段JSP片段中不能包含JSP腳本元素。
WEB容器在處理簡單標簽的標簽體時额港,會把標簽體內(nèi)容用一個JspFragment對象表示扔枫,并調(diào)用標簽處理器對象的setJspBody方法把JspFragment對象傳遞給標簽處理器對象。JspFragment類中只定義了兩個方法锹安,如下所示:
(1) getJspContext
方法
用于返回代表調(diào)用頁面的JspContext對象.
(2) invoke(java.io.Writer out)
方法
用于執(zhí)行JspFragment對象所代表的JSP代碼片段,參數(shù)out用于指定將JspFragment對象的執(zhí)行結(jié)果寫入到哪個輸出流對象中倚舀,如果 傳遞給參數(shù)out的值為null叹哭,則將執(zhí)行結(jié)果寫入到JspContext.getOut()方法返回的輸出流對象中(也就是寫給瀏覽器)
4.3.2 invoke方法詳解
JspFragment.invoke
方法是JspFragment最重要的方法,利用這個方法可以控制是否執(zhí)行和輸出標簽體的內(nèi)容痕貌、是否迭代執(zhí)行標簽體的內(nèi)容或?qū)撕烍w的執(zhí)行結(jié)果進行修改后再輸出风罩。例如:
(1) 在標簽處理器中如果沒有調(diào)用JspFragment.invoke方法,其結(jié)果就相當于忽略標簽體內(nèi)容舵稠;
(2) 在標簽處理器中重復調(diào)用JspFragment.invoke方法超升,則標簽體內(nèi)容將會被重復執(zhí)行入宦;
(3) 若想在標簽處理器中修改標簽體內(nèi)容,只需在調(diào)用invoke方法時指定一個可取出結(jié)果數(shù)據(jù)的輸出流對象(例如StringWriter)室琢,讓標簽體的執(zhí)行結(jié)果輸出到該輸出流對象中乾闰,然后從該輸出流對象中取出數(shù)據(jù)進行修改后再輸出到目標設備,即可達到修改標簽體的目的盈滴。
4.3.3 開發(fā)帶屬性自定義標簽的步驟
要想讓一個自定義標簽具有屬性涯肩,通常需要完成兩個步驟:
(1) 在標簽處理器中編寫每個屬性對應的setter方法。
(2) 在TLD文件中描術(shù)標簽的屬性巢钓。
為自定義標簽定義屬性時病苗,每個屬性都必須按照JavaBean的屬性命名方式,在標簽處理器中定義屬性名對應的setter方法症汹,用來接收 JSP頁面調(diào)用自定義標簽時傳遞進來的屬性值硫朦。如屬性url,在標簽處理器類中就要定義相應的setUrl(String url)
方法背镇。
在標簽處理器中定義相應的set方法后咬展,JSP引擎在解析執(zhí)行開始標簽前,也就是調(diào)用doStartTag方法前芽世,會調(diào)用set屬性方法挚赊,為標簽設置屬性。
tld文件中用于描述標簽屬性的<attribute>
元素說明济瓢,<tag>
元素的<attribute>
子元素用于描述自定義標簽的一個屬性荠割,自定義標簽所具有的每個屬性都要對應一個<attribute>
元素。
<attribute>
元素的子元素說明如下旺矾。
4.3.4 示例:添加屬性控制標簽體的執(zhí)行次數(shù)
首先蔑鹦,寫一個標簽處理類com.test.selfdefine.tag.SimpleTagCountAttribute
,其中包含count屬性和對應的setCount函數(shù)箕宙。
package com.test.selfdefine.tag;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.SkipPageException;
import javax.servlet.jsp.tagext.SimpleTagSupport;
import java.io.IOException;
/**
* Created by chengxia on 2019/10/23.
*/
public class SimpleTagCountAttribute extends SimpleTagSupport {
//定義一個和標簽屬性相對應的成員變量
private int count;
//定義一個和標簽屬性相對應的setter方法
public void setCount(int count){
this.count = count;
}
/* 簡單標簽使用這個方法就可以完成所有的業(yè)務邏輯
* @see javax.servlet.jsp.tagext.SimpleTagSupport#doTag()
* 重寫doTag方法嚎朽,這里根據(jù)屬性count的值,確定標簽體的執(zhí)行次數(shù)柬帕。
*/
@Override
public void doTag() throws JspException, IOException {
for(int i = 0; i < count; i++){
this.getJspBody().invoke(null);
}
}
}
然后哟忍,在自定義標簽配置文件WEB-INF/simpletag.tld
中,添加這個自定義標簽的定義陷寝。
<?xml version="1.0" encoding="utf-8"?>
<taglib xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd"
version="2.1">
<!-- description用來添加對taglib(標簽庫)的描述 -->
<description>SimpleTag Test</description>
<tlib-version>1.0</tlib-version>
<short-name>SimpleTagLibraryTest</short-name>
<!--
為自定義標簽庫設置一個uri锅很,uri以/開頭,/后面的內(nèi)容隨便寫凤跑,如這里的/simpletag 爆安,
在Jsp頁面中引用標簽庫時,需要通過uri找到標簽庫
在Jsp頁面中就要這樣引入標簽庫:<%@taglib uri="/simpletag" prefix="simptag"%>
-->
<uri>/simpletag</uri>
<!-- Invoke 'Generate' action to add tags or functions -->
<!--一個taglib(標簽庫)中包含多個自定義標簽仔引,每一個自定義標簽使用一個tag標記來描述 -->
<!-- 一個tag標記對應一個自定義標簽 -->
<tag>
<description>SimpleTagTest1</description>
<!--
為標簽處理器類配一個標簽名扔仓,在Jsp頁面中使用標簽時是通過標簽名來找到要調(diào)用的標簽處理器類的
通過SimpleTag1就能找到對應的com.test.selfdefine.tag.SimpleTagTest1類
-->
<name>SimpleTag1</name>
<!-- 標簽對應的處理器類-->
<tag-class>com.test.selfdefine.tag.SimpleTagTest1</tag-class>
<!--
tld文件中有四種標簽體類型 :empty JSP scriptless tagdepentend
在簡單標簽(SampleTag)中標簽體body-content的值只允許是empty和scriptless褐奥,不允許設置成JSP,如果設置成JSP就會出現(xiàn)異常
在傳統(tǒng)標簽中標簽體body-content的值只允許是empty和JSP
如果標簽體body-content的值設置成tagdepentend,那么就表示標簽體里面的內(nèi)容是給標簽處理器類使用的翘簇,
例如:開發(fā)一個查詢用戶的sql標簽撬码,此時標簽體重的SQL語句就是給SQL標簽的標簽處理器來使用的
<selftag:sql>SELECT * FROM USER</selftag:sql>
在這種情況下,sql標簽的<body-content>就要設置成tagdepentend缘揪,tagdepentend用得比較少耍群,了解一下即可
-->
<body-content>scriptless</body-content>
</tag>
<tag>
<!-- 標簽名 -->
<name>SimpleTagRepeat</name>
<!-- 標簽處理器類-->
<tag-class>com.test.selfdefine.tag.SimpleTagRepeat</tag-class>
<!-- 標簽體允許的內(nèi)容 ,scriptless表示標簽體的內(nèi)容不允許是java腳本代碼-->
<body-content>scriptless</body-content>
</tag>
<tag>
<!-- 標簽名 -->
<name>SimpleTagModify</name>
<!-- 標簽處理器類-->
<tag-class>com.test.selfdefine.tag.SimpleTagModify</tag-class>
<!-- 標簽體允許的內(nèi)容 找筝,scriptless表示標簽體的內(nèi)容不允許是java腳本代碼-->
<body-content>scriptless</body-content>
</tag>
<tag>
<!-- 標簽名 -->
<name>SimpleTagSkipRest</name>
<!-- 標簽處理器類-->
<tag-class>com.test.selfdefine.tag.SimpleTagSkipRest</tag-class>
<!-- 標簽體允許的內(nèi)容 蹈垢,scriptless表示標簽體的內(nèi)容不允許是java腳本代碼-->
<body-content>scriptless</body-content>
</tag>
<tag>
<!-- 標簽名 -->
<name>SimpleTagCountAttribute</name>
<!-- 標簽處理器類-->
<tag-class>com.test.selfdefine.tag.SimpleTagCountAttribute</tag-class>
<!-- 標簽體允許的內(nèi)容 ,scriptless表示標簽體的內(nèi)容不允許是java腳本代碼-->
<body-content>scriptless</body-content>
<!-- 標簽的屬性描述 -->
<attribute>
<description>描述標簽的count屬性</description>
<name>count</name>
<required>true</required>
<!-- rtexprvalue用來指示標簽的屬性值是否可以是一個表達式袖裕,一般設置為true,true就表示允許標簽的屬性值可以是一個表達式-->
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
</taglib>
最后曹抬,在jsp頁面SimpleTag.jsp
中,添加這個自定義標簽的使用急鳄。
<%--
Created by IntelliJ IDEA.
User: chengxia
Date: 2019/10/10
Time: 8:43 AM
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!-- 使用taglib指令引用simptag標簽庫谤民,標簽庫的前綴(prefix)可以隨便設置,如這里設置成 prefix="simptag" -->
<%@ taglib prefix="simptag" uri="/simpletag" %>
<html>
<head>
<title>Self Define Tag Test</title>
</head>
<body>
<h1>Ha Ha</h1>
<hr/>
<simptag:SimpleTag1>
<h2>This is content for a simple tag.</h2>
</simptag:SimpleTag1>
<hr/>
<simptag:SimpleTagCountAttribute count="2">
<h2>Content for simple tag repeat.</h2>
</simptag:SimpleTagCountAttribute>
</body>
</html>
重啟tomcat服務器之后疾宏,訪問http://localhost:8080/SimpleTag.jsp
张足,效果如下。
注意:如果標簽的屬性值是8種基本數(shù)據(jù)類型坎藐,那么在JSP頁面在傳遞字符串時为牍,JSP引擎會自動轉(zhuǎn)換成相應的類型,但如果標簽的屬性值是復合數(shù)據(jù)類型岩馍,那么JSP引擎是無法自動轉(zhuǎn)換的碉咆。但如果標簽的屬性值是復合數(shù)據(jù)類型,那么JSP引擎是無法自動轉(zhuǎn)換的蛀恩。如果一定要給標簽的復合屬性賦值疫铜,那么可以采用表達式的方式給復合屬性賦值。如下:
<%
Date d = new Date();
request.setAttribute("date", d);
%>
<simptag:SimpleTagDateAttribute date="${date}"/>
<simptag:SimpleTagDateAttribute date="<%=new Date()%>"/>
注:jsp中${}
是EL表達式的常規(guī)表示方式双谆。目的是為了獲取{}
中指定的對象(參數(shù)刃永、對象等)的值微王。如${name}
注祖,就是從當前頁面起開始搜尋name變量念恍,搜尋的范圍依次是:page、request趣避、session、application新翎,如果未搜索到程帕,即會返回null值住练。
5、標簽開發(fā)注意事項
在傳統(tǒng)標簽中標簽體body-content的值允許是empty愁拭、JSP讲逛、scriptless、tagdependent岭埠,body-content的值如果是設置成JSP盏混,那么表示該標簽是有標簽體的,并且標簽體的內(nèi)容可以是任意的惜论,包括java代碼许赃,如果是設置成scriptless,那么表示該標簽是有標簽體的馆类,但是標簽體的內(nèi)容不能是java代碼混聊。如果傳統(tǒng)標簽和簡單標簽的標簽體body-content的值設置成tagdependent,那么就表示標簽體里面的內(nèi)容是給標簽處理器類使用的乾巧。
在簡單標簽(SampleTag)中標簽體body-content的值只允許是empty句喜、scriptless、tagdependent沟于,不允許設置成JSP咳胃,否則會拋出異常:The TLD for the class com.test.selfdefine.tag.SimpleTagModify specifies an invalid body-content (JSP) for a SimpleTag.
。