JSTL標簽庫只提供了簡單的輸出等功能,沒有實現任何的HTML代碼封裝,并且某些復雜類型轉換,或者邏輯處理的時候,JSTL標簽庫完成不了,需要自定義標簽!
目標:使用標簽輸出客戶機的IP地址!
按照步驟來:首先編寫一個實現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在問題來了,在Tag重寫的方法好像不能直接獲取到request對象啊。經過我一番仔細的觀察,發現了下面這個方法: @Override public void setPageContext(PageContext pageContext) { }既然能獲取到pageContext對象,那么其他8大內置對象還不是隨隨便便?于是乎,我就定義一個成員變量pageContext,在setPageContext()方法中傳遞過來的pageContext賦值給我定義的成員變量即可! PRivate PageContext pageContext = null; @Override public void setPageContext(PageContext pageContext) { this.pageContext = pageContext; }好的,看回我們的需求:使用標簽輸出客戶機的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文件,描述實現Tag接口的Java類【標簽處理類】。 <?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>下面我們來測試一下看能不能用看完上面的程序,大部分人都是懵逼的。因為還不知道它具體是怎么用的,調用順序是什么。
首先我們來看一下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(); }上面程序的執行流程: JSP引擎遇到自定義標簽,首先創建標簽處理器類的實例對象。JSP引擎實例化完標簽處理器類后,調用setPageContext()方法,將pageContext對象傳遞給標簽處理器類,使得標簽處理器類可以通過pageContext對象與JSP頁面進行通信!setPageContext()方法執行完后,調用setParent()方法,將當前標簽的父標簽傳遞給當前處理器類,如果當前標簽沒有父標簽,則傳入null當WEB容器執行到自定義標簽的開始標記時,調用doStartTag()方法。當WEB容器執行到自定義標簽的結束標記時,調用doEndTag()方法。一般來說,當WEB容器執行完自定義標簽后,標簽處理器類會駐留在內存中,直至停止WEB應用時,WEB容器才會調用release()方法我們現在已經清楚了方法的執行順序了,可Tag接口的源碼還有4個變量阿,它們是用來做什么的呢?我們在編寫JSP頁面時,經常需要在頁面中引入一些邏輯,例如:
控制JSP頁面某一部分(標簽體)是否執行控制整個JSP頁面是否執行控制JSP頁面內容重復執行修改JSP頁面內容輸出再看回4個變量的名字,我們可以發現,這4個變量就是用來做邏輯判斷的!
我們來測試一下吧,在doEndTag()方法中,返回的是SKIP_PAGE變量,看下會怎么樣!
@Override public int doEndTag() throws JspException { return SKIP_PAGE; }我們再來看一看效果:大部分時候我們都不需要實現Tag接口來編寫自定義標簽,TagSupport是Tag的一個模板類,實現了pageContext,parent的getter、setter方法以及一些其他的功能。我們要做的就是重寫doStartTag()和doEndTag()方法
下面我們就來簡單使用一下吧:
繼承TagSupport類,重寫doStartTag()方法,比直接實現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>效果:上面我們編寫的自定義標簽都沒有附帶屬性的,我們在使用core標簽庫的時候,標簽一般都帶有屬性。
其實JSTL標簽庫的原理就是自定義標簽,把自定義標簽搞明白了,對JSTL標簽庫的使用就有更好的理解了!
想要自定義標簽帶有屬性也非常簡單,只要在標簽處理器類上加一個成員變量和setter、getter(),再在tld文件中描述下該屬性即可!它的原理是這樣的:當標簽使用到屬性的時候,引擎就會調用它的setter()方法
下面我想要完成的功能是:使用標簽的人,傳入一個字符串格式就可以顯示想要的格式日期
編寫標簽處理器類,增加一個成員變量以及對應的setter、getter方法
public class Demo1 extends TagSupport { //創建成員對象,對應的setter、getter方法 private String format = null; @Override public int doStartTag() throws JspException { //創建日期格式化對象 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文件中描述標簽和屬性,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>我們來看一下效果:前面我們已經使用到了帶標簽體的自定義標簽了,前面的都是只能直接輸出而得不到標簽體的內容,既然得不到標簽體的內容,就更別說修改標簽體了!
此時,我們就需要BodyTag接口的支持了!它專門用來處理帶標簽體的標簽,下面我們來看一下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變量【一個已經標識過時了】,多了setBodyContent和doInitBody()兩個方法其實使用BodyTag十分簡單
如果doStartTag()方法返回的是EVAL_BODY_BUFFERED,把標簽體的內容緩存起來接著調用setBodyContent()方法和doInitBody()方法,封裝標簽體的內容到BodyContent對象中接著調用doEndTag()方法對于標簽體的內容,我們可以通過getBodyContenet()來獲??!再看回上面的關系圖,BodyTag實現了IterationTag和Tag接口,如果直接實現BodyTag接口做開發,要實現的方法就太多了。一般我們使用繼承BodyTag的BodyTagSupport來做開發
原來BodyContent繼承著JspWriter,它與JspWriter最大的區別是:BodyContent類的任何寫入的內容并不自動地向頁面輸出!
我們一般使用BodyContent都使用兩個方法:
//將數據轉變成Reader對象 public abstract Reader getReader(); //將數據轉變成String對象 public abstract String getString();再從關系圖我們可以看初,BodyTagSupport繼承了TagSupport類實現了BodyTag接口,可以說:BodyTagSupport有著前面講的接口和類的所有功能!。
下面我們來使用下BodyTagSupport將標簽體的內容轉成是小寫的:
標簽處理器類
public class Demo1 extends BodyTagSupport { @Override public int doStartTag() throws JspException { //想要獲取到標簽體的內容,就要返回EVAL_BODY_BUFFERED變量 return EVAL_BODY_BUFFERED; } @Override public int doEndTag() throws JspException { //獲取到標簽體的內容 String value = bodyContent.getString(); //將標簽體的內容轉成小寫并輸出 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>效果:新聞熱點
疑難解答