国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > JSP > 正文

JSP第六篇【自定義標(biāo)簽之傳統(tǒng)標(biāo)簽】

2019-11-10 19:40:59
字體:
供稿:網(wǎng)友

為什么要使用自定義標(biāo)簽?

JSTL標(biāo)簽庫只提供了簡單的輸出等功能,沒有實現(xiàn)任何的HTML代碼封裝,并且某些復(fù)雜類型轉(zhuǎn)換,或者邏輯處理的時候,JSTL標(biāo)簽庫完成不了,需要自定義標(biāo)簽!

編寫自定義標(biāo)簽的步驟:

編寫一個實現(xiàn)Tag接口的java類【標(biāo)簽處理器類】在WEB-INF目錄下創(chuàng)建tld(Tag Library Descriptor)文件,在tld文件中對標(biāo)簽處理類(實現(xiàn)Tag接口的Java類)進行描述

快速入門

目標(biāo):使用標(biāo)簽輸出客戶機的IP地址!

按照步驟來:首先編寫一個實現(xiàn)Tag接口的Java類

public class showIp implements Tag { @Override public void setPageContext(PageContext pageContext) { } @Override public void setParent(Tag tag) { } @Override public Tag getParent() { return null; } @Override public int doStartTag() throws jspException { return 0; } @Override public int doEndTag() throws JspException { return 0; } @Override public void release() { } }既然要獲取到客戶機的IP地址,那么request對象是必不可少的?,F(xiàn)在問題來了,在Tag重寫的方法好像不能直接獲取到request對象啊。經(jīng)過我一番仔細的觀察,發(fā)現(xiàn)了下面這個方法: @Override public void setPageContext(PageContext pageContext) { }既然能獲取到pageContext對象,那么其他8大內(nèi)置對象還不是隨隨便便?于是乎,我就定義一個成員變量pageContext,在setPageContext()方法中傳遞過來的pageContext賦值給我定義的成員變量即可PRivate PageContext pageContext = null; @Override public void setPageContext(PageContext pageContext) { this.pageContext = pageContext; }好的,看回我們的需求:使用標(biāo)簽輸出客戶機的IP地址。在上面剩余5個方法中,最有可能就是在doStartTag()方法中編寫代碼! @Override public int doStartTag() throws JspException { //獲取到request對象 HttpServletRequest httpServletRequest = (HttpServletRequest) pageContext.getRequest(); //獲取到客戶機的ip地址 String ip = httpServletRequest.getRemoteAddr(); //獲取輸出到瀏覽器的對象 JspWriter jspWriter = pageContext.getOut(); //下面的異常只能捕獲,因為子類的異常不能比父類多 try { jspWriter.write(ip); } catch (IOException e) { e.printStackTrace(); } return 0; }接著,編寫tld文件,描述實現(xiàn)Tag接口的Java類【標(biāo)簽處理類】。 <?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"> <tlib-version>1.0</tlib-version> <short-name>zhongfucheng</short-name> <uri>/zhongfucheng</uri> <!-- Invoke 'Generate' action to add tags or functions --> <tag> <name>viewIp</name> <tag-class>tag.showIp</tag-class> <body-content>empty</body-content> </tag> </taglib>下面我們來測試一下看能不能用


標(biāo)簽處理類詳細說明

看完上面的程序,大部分人都是懵逼的。因為還不知道它具體是怎么用的,調(diào)用順序是什么

首先我們來看一下Tag接口的源碼! public interface Tag extends JspTag { int SKIP_BODY = 0; int EVAL_BODY_INCLUDE = 1; int SKIP_PAGE = 5; int EVAL_PAGE = 6; void setPageContext(PageContext var1); void setParent(Tag var1); Tag getParent(); int doStartTag() throws JspException; int doEndTag() throws JspException; void release(); }上面程序的執(zhí)行流程: JSP引擎遇到自定義標(biāo)簽,首先創(chuàng)建標(biāo)簽處理器類的實例對象。JSP引擎實例化完標(biāo)簽處理器類后,調(diào)用setPageContext()方法,將pageContext對象傳遞給標(biāo)簽處理器類,使得標(biāo)簽處理器類可以通過pageContext對象與JSP頁面進行通信!setPageContext()方法執(zhí)行完后,調(diào)用setParent()方法,將當(dāng)前標(biāo)簽的父標(biāo)簽傳遞給當(dāng)前處理器類,如果當(dāng)前標(biāo)簽沒有父標(biāo)簽,則傳入null當(dāng)WEB容器執(zhí)行到自定義標(biāo)簽的開始標(biāo)記時,調(diào)用doStartTag()方法。當(dāng)WEB容器執(zhí)行到自定義標(biāo)簽的結(jié)束標(biāo)記時,調(diào)用doEndTag()方法。一般來說,當(dāng)WEB容器執(zhí)行完自定義標(biāo)簽后,標(biāo)簽處理器類會駐留在內(nèi)存中,直至停止WEB應(yīng)用時,WEB容器才會調(diào)用release()方法

這里寫圖片描述


我們現(xiàn)在已經(jīng)清楚了方法的執(zhí)行順序了,可Tag接口的源碼還有4個變量阿,它們是用來做什么的呢?我們在編寫JSP頁面時,經(jīng)常需要在頁面中引入一些邏輯,例如:

控制JSP頁面某一部分(標(biāo)簽體)是否執(zhí)行控制整個JSP頁面是否執(zhí)行控制JSP頁面內(nèi)容重復(fù)執(zhí)行修改JSP頁面內(nèi)容輸出

再看回4個變量的名字,我們可以發(fā)現(xiàn),這4個變量就是用來做邏輯判斷的!

我們來測試一下吧,在doEndTag()方法中,返回的是SKIP_PAGE變量,看下會怎么樣!

@Override public int doEndTag() throws JspException { return SKIP_PAGE; }我們再來看一看效果:

好像是沒什么區(qū)別!我們再查看一下源代碼,發(fā)現(xiàn)執(zhí)行完標(biāo)簽后,后面的代碼全都沒有執(zhí)行!

doStartTag()方法使用的是SKIP_BODY和EVAL_BODY_INCLUDE這兩個變量,判斷是否執(zhí)行標(biāo)簽體的內(nèi)容。doEndTag()方法使用的是SKIP_PAGE和EVAL_PAGE這兩個變量,判斷是否執(zhí)行剩下頁面的內(nèi)容控制JSP頁面內(nèi)容重復(fù)執(zhí)行和修改JSP頁面內(nèi)容輸出后面會有!

tld文件詳細說明

這里寫圖片描述

這里寫圖片描述

首先我們來看一下tld文件當(dāng)前用到的內(nèi)容吧! <tlib-version>1.0</tlib-version> <short-name>myshortname</short-name> <uri>http://mycompany.com</uri> <tag> <name></name> <tag-class></tag-class> <body-content></body-content> </tag>我們一個一個來看: shortname推薦使用prefixuri就是引入這個標(biāo)簽庫使用的uriname為標(biāo)簽名tagclass為實現(xiàn)類bodycontent為標(biāo)簽體的限制,它有4個值: EMPTY【不允許有標(biāo)簽體】,JSP【允許有JSP代碼】 ,scriptless【不允許有腳本代碼(也就是<%%>),允許有EL表達式,文本,JSP行為】 , tagdepentend【標(biāo)簽體內(nèi)的JSP代碼不會被解析,直接輸出文本】

TagSupport類

大部分時候我們都不需要實現(xiàn)Tag接口來編寫自定義標(biāo)簽,TagSupport是Tag的一個模板類,實現(xiàn)了pageContext,parent的getter、setter方法以及一些其他的功能。我們要做的就是重寫doStartTag()和doEndTag()方法

下面我們就來簡單使用一下吧:

繼承TagSupport類,重寫doStartTag()方法,比直接實現(xiàn)Tag接口簡潔很多!

public class Demo1 extends TagSupport { @Override public int doStartTag() throws JspException { //獲取到request對象 HttpServletRequest httpServletRequest = (HttpServletRequest) pageContext.getRequest(); String method = httpServletRequest.getMethod(); JspWriter jspWriter = pageContext.getOut(); try { jspWriter.write(method); } catch (IOException e) { e.printStackTrace(); } return 0; } }在tld文件中描述一把: <tag> <name>showMethod</name> <tag-class>tag.Demo1</tag-class> <body-content>empty</body-content> </tag>效果:

帶屬性的標(biāo)簽

上面我們編寫的自定義標(biāo)簽都沒有附帶屬性的,我們在使用core標(biāo)簽庫的時候,標(biāo)簽一般都帶有屬性。

其實JSTL標(biāo)簽庫的原理就是自定義標(biāo)簽,把自定義標(biāo)簽搞明白了,對JSTL標(biāo)簽庫的使用就有更好的理解了

想要自定義標(biāo)簽帶有屬性也非常簡單,只要在標(biāo)簽處理器類上加一個成員變量和setter、getter(),再在tld文件中描述下該屬性即可!它的原理是這樣的:當(dāng)標(biāo)簽使用到屬性的時候,引擎就會調(diào)用它的setter()方法

下面我想要完成的功能是:使用標(biāo)簽的人,傳入一個字符串格式就可以顯示想要的格式日期

編寫標(biāo)簽處理器類,增加一個成員變量以及對應(yīng)的setter、getter方法

public class Demo1 extends TagSupport { //創(chuàng)建成員對象,對應(yīng)的setter、getter方法 private String format = null; @Override public int doStartTag() throws JspException { //創(chuàng)建日期格式化對象 SimpleDateFormat simpleDateFormat = new SimpleDateFormat(format); //格式化日期并向瀏覽器輸出 try { pageContext.getOut().write(simpleDateFormat.format(new Date())); } catch (IOException e) { e.printStackTrace(); } return 0; } public String getFormat() { return format; } public void setFormat(String format) { this.format = format; } }在tld文件中描述標(biāo)簽和屬性,name代表的是屬性的名字,required代表的是是否為必須,rtexprvalue代表能否使用EL表達式 <tag> <name>formatDate</name> <tag-class>tag.Demo1</tag-class> <body-content>empty</body-content> <attribute> <name>format</name> <required>true</required> <rtexprvalue>true</rtexprvalue> </attribute> </tag>我們來看一下效果:


標(biāo)簽的繼承關(guān)系

在深入講解之前,我們先來看一下各種Tag接口、類之間的關(guān)系,這樣學(xué)習(xí)下去才不會暈!

IterationTag說明

我們已經(jīng)使用過了Tag接口和TagSupport類了。接下來我們看一下IterationTag是什么玩意。 public interface IterationTag extends Tag { int EVAL_BODY_AGAIN = 2; int doAfterBody() throws JspException; }從關(guān)系圖我們也可以看出,IterationTag接口實現(xiàn)了Tag接口,InterationTag接口和Tag接口最主要的區(qū)別就是多了個doAfterBody()方法和EVAL_BODY_AGAIN變量理解起來也很簡單:當(dāng)doAfterBody()返回的是EVAL_BODY_AGAIN變量,那么標(biāo)簽體的內(nèi)容就一直循環(huán)!當(dāng)然了,TagSupport也實現(xiàn)了Iteration接口,也就是說TagSupport類也能完成Iteration接口的事情!我們來使用一下吧: public class Demo1 extends TagSupport { @Override public int doStartTag() throws JspException { try { pageContext.getOut().write("hello"); } catch (IOException e) { e.printStackTrace(); } //執(zhí)行標(biāo)簽體 return EVAL_BODY_INCLUDE; } @Override public int doAfterBody() throws JspException { //標(biāo)簽體不斷循環(huán),直到doAfterBody()返回的是SKIP_BODY return EVAL_BODY_AGAIN; } }tld文件中描述,既然標(biāo)簽體有內(nèi)容,就不能用empty了! <tag> <name>foreverEval</name> <tag-class>tag.Demo1</tag-class> <body-content>tagdependent</body-content> </tag>注意看橫向的滑輪,已經(jīng)死循環(huán)輸出了:

doAfterBody()中只要返回的是SKPI_BODY就退出循環(huán),執(zhí)行doEndTag()方法 //定義一個變量,規(guī)定標(biāo)簽體循環(huán)的次數(shù) int x = 0; @Override public int doStartTag() throws JspException { try { pageContext.getOut().write("hello"); } catch (IOException e) { e.printStackTrace(); } //執(zhí)行標(biāo)簽體 return EVAL_BODY_INCLUDE; } @Override public int doAfterBody() throws JspException { x++; if (x >= 10) { return SKIP_BODY; } //標(biāo)簽體不斷循環(huán),直到doAfterBody()返回的是SKIP_BODY return EVAL_BODY_AGAIN; }現(xiàn)在我們已經(jīng)能控制循環(huán)的次數(shù)了!


BodyTag說明

前面我們已經(jīng)使用到了帶標(biāo)簽體的自定義標(biāo)簽了,前面的都是只能直接輸出而得不到標(biāo)簽體的內(nèi)容,既然得不到標(biāo)簽體的內(nèi)容,就更別說修改標(biāo)簽體了!

此時,我們就需要BodyTag接口的支持了!它專門用來處理帶標(biāo)簽體的標(biāo)簽,下面我們來看一下BodyTag的源碼! public interface BodyTag extends IterationTag { /** @deprecated */ int EVAL_BODY_TAG = 2; int EVAL_BODY_BUFFERED = 2; void setBodyContent(BodyContent var1); void doInitBody() throws JspException; }BodyTag多了EVAL_BODY_BUFFERED變量【一個已經(jīng)標(biāo)識過時了】,多了setBodyContent和doInitBody()兩個方法

其實使用BodyTag十分簡單

如果doStartTag()方法返回的是EVAL_BODY_BUFFERED,把標(biāo)簽體的內(nèi)容緩存起來接著調(diào)用setBodyContent()方法和doInitBody()方法,封裝標(biāo)簽體的內(nèi)容到BodyContent對象中接著調(diào)用doEndTag()方法對于標(biāo)簽體的內(nèi)容,我們可以通過getBodyContenet()來獲??!

再看回上面的關(guān)系圖,BodyTag實現(xiàn)了IterationTag和Tag接口,如果直接實現(xiàn)BodyTag接口做開發(fā),要實現(xiàn)的方法就太多了。一般我們使用繼承BodyTag的BodyTagSupport來做開發(fā)


BodyTagSupport說明

首先來看一下源代碼吧: public class BodyTagSupport extends TagSupport implements BodyTag { protected BodyContent bodyContent; public BodyTagSupport() { } public int doStartTag() throws JspException { return 2; } public int doEndTag() throws JspException { return super.doEndTag(); } public void setBodyContent(BodyContent b) { this.bodyContent = b; } public void doInitBody() throws JspException { } public int doAfterBody() throws JspException { return 0; } public void release() { this.bodyContent = null; super.release(); } public BodyContent getBodyContent() { return this.bodyContent; } public JspWriter getPreviousOut() { return this.bodyContent.getEnclosingWriter(); } }可以發(fā)現(xiàn):BodyTagSupport主要擴充了以下的內(nèi)容: 把BodyContent直接定義為成員變量,在獲取標(biāo)簽體內(nèi)容的時候就不需要通過getBodyContent()獲取了提供獲取JspWriter的方法,不需要從pageConext中獲取了以上的兩個擴充都簡化了我們的代碼書寫! protected BodyContent bodyContent; public JspWriter getPreviousOut() { return this.bodyContent.getEnclosingWriter(); }從BodyTag接口中,我就說到了:標(biāo)簽體的內(nèi)容封裝到了BodyContent類中,那么BodyContent類究竟是什么?我們來看一下源碼: public abstract class BodyContent extends JspWriter { private JspWriter enclosingWriter; protected BodyContent(JspWriter e) { super(-2, false); this.enclosingWriter = e; } public void flush() throws IOException { throw new IOException("Illegal to flush within a custom tag"); } public void clearBody() { try { this.clear(); } catch (IOException var2) { throw new Error("internal error!;"); } } public abstract Reader getReader(); public abstract String getString(); public abstract void writeOut(Writer var1) throws IOException; public JspWriter getEnclosingWriter() { return this.enclosingWriter; } }

原來BodyContent繼承著JspWriter,它與JspWriter最大的區(qū)別是:BodyContent類的任何寫入的內(nèi)容并不自動地向頁面輸出!

我們一般使用BodyContent都使用兩個方法:

//將數(shù)據(jù)轉(zhuǎn)變成Reader對象 public abstract Reader getReader(); //將數(shù)據(jù)轉(zhuǎn)變成String對象 public abstract String getString();

再從關(guān)系圖我們可以看初,BodyTagSupport繼承了TagSupport類實現(xiàn)了BodyTag接口,可以說:BodyTagSupport有著前面講的接口和類的所有功能!。

下面我們來使用下BodyTagSupport將標(biāo)簽體的內(nèi)容轉(zhuǎn)成是小寫的

標(biāo)簽處理器類

public class Demo1 extends BodyTagSupport { @Override public int doStartTag() throws JspException { //想要獲取到標(biāo)簽體的內(nèi)容,就要返回EVAL_BODY_BUFFERED變量 return EVAL_BODY_BUFFERED; } @Override public int doEndTag() throws JspException { //獲取到標(biāo)簽體的內(nèi)容 String value = bodyContent.getString(); //將標(biāo)簽體的內(nèi)容轉(zhuǎn)成小寫并輸出 try { this.getPreviousOut().write(value.toLowerCase()); } catch (IOException e) { e.printStackTrace(); } return super.doEndTag(); } }tld文件: <tag> <name>BodyContentToLowerCase</name> <tag-class>tag.Demo1</tag-class> <body-content>tagdependent</body-content> </tag>效果:



發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 五华县| 城固县| 鹤岗市| 吴忠市| 中宁县| 潞西市| 恩施市| 穆棱市| 彩票| 呈贡县| 上高县| 兰州市| 台中市| 南部县| 新河县| 铜陵市| 博爱县| 鹤庆县| 岑溪市| 天等县| 湘阴县| 耒阳市| 诸暨市| 延庆县| 隆昌县| 德化县| 正镶白旗| 克山县| 佛学| 阿巴嘎旗| 堆龙德庆县| 进贤县| 大埔区| 句容市| 南木林县| 沙湾县| 应用必备| 巴林左旗| 明光市| 福建省| 庄河市|