JSTL標籤庫只提供了簡單的輸出等功能,沒有實現任何的HTML代碼封裝,而且某些複雜類型轉換,或者邏輯處理的時候,JSTL標籤庫完成不了,須要自定義標籤!java
目標:使用標籤輸出客戶機的IP地址!web
按照步驟來:首先編寫一個實現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() { } }
@Override public void setPageContext(PageContext pageContext) { }
private PageContext pageContext = null; @Override public void setPageContext(PageContext pageContext) { this.pageContext = pageContext; }
@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; }
<?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>
看完上面的程序,大部分人都是懵逼的。由於還不知道它具體是怎麼用的,調用順序是什麼。緩存
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(); }
咱們如今已經清楚了方法的執行順序了,可Tag接口的源碼還有4個變量阿,它們是用來作什麼的呢?咱們在編寫JSP頁面時,常常須要在頁面中引入一些邏輯,例如:微信
再看回4個變量的名字,咱們能夠發現,這4個變量就是用來作邏輯判斷的!less
咱們來測試一下吧,在doEndTag()方法中,返回的是SKIP_PAGE變量,看下會怎麼樣!jsp
@Override public int doEndTag() throws JspException { return SKIP_PAGE; }
<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>
大部分時候咱們都不須要實現Tag接口來編寫自定義標籤,TagSupport是Tag的一個模板類,實現了pageContext,parent的getter、setter方法以及一些其餘的功能。咱們要作的就是重寫doStartTag()和doEndTag()方法ide
下面咱們就來簡單使用一下吧:學習
繼承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; } }
<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; } }
<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>
public interface IterationTag extends Tag { int EVAL_BODY_AGAIN = 2; int doAfterBody() throws JspException; }
public class Demo1 extends TagSupport { @Override public int doStartTag() throws JspException { try { pageContext.getOut().write("hello"); } catch (IOException e) { e.printStackTrace(); } //執行標籤體 return EVAL_BODY_INCLUDE; } @Override public int doAfterBody() throws JspException { //標籤體不斷循環,直到doAfterBody()返回的是SKIP_BODY return EVAL_BODY_AGAIN; } }
<tag> <name>foreverEval</name> <tag-class>tag.Demo1</tag-class> <body-content>tagdependent</body-content> </tag>
//定義一個變量,規定標籤體循環的次數 int x = 0; @Override public int doStartTag() throws JspException { try { pageContext.getOut().write("hello"); } catch (IOException e) { e.printStackTrace(); } //執行標籤體 return EVAL_BODY_INCLUDE; } @Override public int doAfterBody() throws JspException { x++; if (x >= 10) { return SKIP_BODY; } //標籤體不斷循環,直到doAfterBody()返回的是SKIP_BODY return EVAL_BODY_AGAIN; }
前面咱們已經使用到了帶標籤體的自定義標籤了,前面的都是隻能直接輸出而得不到標籤體的內容,既然得不到標籤體的內容,就更別說修改標籤體了!
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十分簡單
再看回上面的關係圖,BodyTag實現了IterationTag和Tag接口,若是直接實現BodyTag接口作開發,要實現的方法就太多了。通常咱們使用繼承BodyTag的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(); } }
protected BodyContent bodyContent; public JspWriter getPreviousOut() { return this.bodyContent.getEnclosingWriter(); }
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最大的區別是: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(); } }
<tag> <name>BodyContentToLowerCase</name> <tag-class>tag.Demo1</tag-class> <body-content>tagdependent</body-content> </tag>
若是文章有錯的地方歡迎指正,你們互相交流。習慣在微信看技術文章的同窗,能夠關注微信公衆號:Java3y.