JSTL標籤庫只提供了簡單的輸出等功能,沒有實現任何的HTML代碼封裝,而且某些複雜類型轉換,或者邏輯處理的時候,JSTL標籤庫完成不了,須要自定義標籤!php
目標:使用標籤輸出客戶機的IP地址!java
按照步驟來:首先編寫一個實現Tag接口的Java類web
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個變量就是用來作邏輯判斷的!微信
咱們來測試一下吧,在doEndTag()方法中,返回的是SKIP_PAGE變量,看下會怎麼樣!less
@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()方法jsp
下面咱們就來簡單使用一下吧: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.