上一篇博客中我已經講解了傳統標籤,想要開發自定義標籤,大多數狀況下都要重寫doStartTag(),doAfterBody()和doEndTag()方法,而且還要知道SKIP_BODY,EVAL_BODY等等的變量表明着什麼,在什麼方法中使用。這樣實在是太麻煩了!javascript
所以,爲了簡化標籤開發的複雜度,在JSP 2.0中定義了一個更爲簡單、便於編寫和調用的SimpleTag接口來實現標籤的功能。。php
通常來講,實現了SimpeTag接口的標籤稱之爲簡單標籤html
public interface SimpleTag extends JspTag { void doTag() throws JspException, IOException; void setParent(JspTag var1); JspTag getParent(); void setJspContext(JspContext var1); void setJspBody(JspFragment var1); }
void doTag() throws JspException, IOException; void setJspContext(JspContext var1); void setJspBody(JspFragment var1);
目標:傳入字符串格式就能夠顯示想要的格式日期,對比以前傳統標籤的,看有什麼不一樣之處java
標籤處理器類:數組
public class Demo1 extends SimpleTagSupport { String format = null; @Override public void doTag() throws JspException, IOException { SimpleDateFormat simpleDateFormat = new SimpleDateFormat(format); this.getJspContext().getOut().write(simpleDateFormat.format(new Date())); } 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>tagdependent</body-content> <attribute> <name>format</name> <required>true</required> <rtexprvalue>true</rtexprvalue> </attribute> </tag>
在咱們講解傳統標籤的時候,配合着SKIP_BODY、SKIP_PAGE等變量能夠實現以下的功能:瀏覽器
簡單標籤可沒有這些變量呀,那它怎麼才能實現上面那些功能呢?markdown
在doTag方法中能夠拋出javax.servlet.jsp.SkipPageException異常,用於通知WEB容器再也不執行JSP頁面中位於結束標記後面的內容,這等效於在傳統標籤的doEndTag方法中返回Tag.SKIP_PAGE常量的狀況,咱們來測試一下,在上面例子的代碼中添加:app
throw new SkipPageException();
SimpleTagSupport也能夠帶標籤體,可是處理方法和傳統標籤徹底不一樣。less
咱們來看一下JspFragment對象的源碼吧:jsp
public abstract class JspFragment { public JspFragment() { } public abstract void invoke(Writer var1) throws JspException, IOException; public abstract JspContext getJspContext(); }
JspFragment對象十分簡單,重要的只有invoke(Writer var1)方法(獲取JspContext對象並不重要,在標籤描述起上就能夠獲取到了)
public abstract void invoke(java.io.Writer out) :
public class Demo1 extends SimpleTagSupport { @Override public void doTag() throws JspException, IOException { //獲得表明標籤體的對象 JspFragment jspFragment = getJspBody(); //invoke方法接收的是一個Writer,若是爲null,就表明着JspWriter(),將標籤體的數據寫給瀏覽器! jspFragment.invoke(null); } }
既然標籤體的內容是經過JspFragment對象的invoke()方法寫給瀏覽器的,那麼那麼那麼,我只要控制好invoke()方法,我想幹什麼就幹什麼!
也就是說:
來來來,咱們來試驗一下:
public void doTag() throws JspException, IOException { //獲得表明標籤體的對象 JspFragment jspFragment = getJspBody(); //jspFragment.invoke(null); }
標籤體的內容沒有輸出
調用兩次invoke()方法
public void doTag() throws JspException, IOException { //獲得表明標籤體的對象 JspFragment jspFragment = getJspBody(); jspFragment.invoke(null); jspFragment.invoke(null); }
//獲得表明標籤體的對象 JspFragment jspFragment = getJspBody(); //建立能夠存儲字符串的Writer對象 StringWriter stringWriter = new StringWriter(); //invoke()方法把標籤體的數據都寫給流對象中 jspFragment.invoke(stringWriter); //把流對象的數據取出來,流對象的數據就是標籤體的內容 String value = stringWriter.toString(); //將數據改爲是大寫的,寫到瀏覽器中 getJspContext().getOut().write(value.toUpperCase());
咱們能夠發現,傳統標籤能完成的功能,簡單標籤均可以完成,而且更爲簡單!
既然咱們學了簡單標籤,咱們就用簡單標籤來作開發吧!
在講解request對象的時候,咱們講解過怎麼實現防盜鏈的功能。如今咱們使用標籤來進行防盜鏈!
模擬下場景:1.jsp頁面是海賊王資源,2.jsp頁面提示非法盜鏈,index1.jsp是個人首頁。別人想要看個人海賊王資源,就必須經過個人首頁點進去看,不然就是非法盜鏈!
@Override public void doTag() throws JspException, IOException { //若是想要作成更加靈活的,就把站點設置和資源設置成標籤屬性傳遞進來! //等會咱們要獲取獲得request對象,須要使用到JspContext的子類PageContext PageContext pageContext = (PageContext) this.getJspContext(); //獲取request對象 HttpServletRequest httpServletRequest = (HttpServletRequest) pageContext.getRequest(); //獲取到referer String referer = httpServletRequest.getHeader("Referer"); //獲取到response對象,等會若是是非法盜鏈,就重定向別的頁面上 HttpServletResponse httpServletResponse = (HttpServletResponse) pageContext.getResponse(); //非法盜鏈! if (referer == null || !referer.startsWith("http://localhost:8080/zhongfucheng")) { //2.jsp提示了非法盜鏈! httpServletResponse.sendRedirect("/zhongfucheng/2.jsp"); //不執行頁面下面的內容了,保護頁面 throw new SkipPageException(); } }
<zhongfucheng:Referer/> <html> <head> <title></title> </head> <body> 海賊王最新資源 </body> </html>
<h1>這是首頁!</h1> <a href="${pageContext.request.contextPath}/1.jsp"> 海賊王最新資源</a>
<body> 你是非法盜鏈的!!!!!! </body>
在JSTL中,咱們已經使用過了<c:if/>
標籤了,如今咱們學習了自定義標籤,能夠開發相似於JSTL的if標籤了!
既然是if標籤,那麼就須要編寫帶屬性和帶標籤體的標籤(須要判斷是true仍是false呀!,經過判斷是否爲真值來決定是否執行標籤體的內容)
public class Demo1 extends SimpleTagSupport { //定義一個Boolean類型的變量 boolean test ; @Override public void doTag() throws JspException, IOException { //獲取到表明標籤體內容的對象 JspFragment jspFragment = this.getJspBody(); //若是爲真值才執行標籤體的內容 if (test == true) { jspFragment.invoke(null); } } public boolean isTest() { return test; } public void setTest(boolean test) { this.test = test; } }
<tag> <name>if</name> <tag-class> tag.Demo1</tag-class> <body-content>scriptless</body-content> <attribute> <name>test</name> <required>true</required> <rtexprvalue>true</rtexprvalue> </attribute> </tag>
forEach標籤最基本的功能:遍歷集合、數組
public class Demo2 extends SimpleTagSupport { //遍歷的是List集合,因而標籤的屬性就爲List private List items; //遍歷出來的對象就用Object存着,由於咱們不知道List集合保存的是什麼元素 private Object var; @Override public void doTag() throws JspException, IOException { //獲取到迭代器 Iterator iterator = items.iterator(); //遍歷集合 while (iterator.hasNext()) { //獲取到集合的元素 var = iterator.next(); //.....var屬性表明的就是集合的元素,如今問題來了,好像在標籤體內沒法獲取到這個對象.... //作到這裏完成不下去了.... } } public void setItems(List items) { this.items = items; } public void setVar(Object var) { this.var = var; } }
上面的思路是正常的,可是作不下去!咱們換一個思路唄。上面的問題主要是在標籤體獲取不到被遍歷出來的對象!
咱們這樣作:把var定義成String類型的,若是遍歷獲得對象了,就設置PageContext的屬性,var爲關鍵字,對象爲值。在標籤體用EL表達式搜索以var爲關鍵字的對象!每遍歷出一個對象,就執行一次標籤體!
public class Demo1 extends SimpleTagSupport { //遍歷的是List集合,定義List集合成員變量 private List items; //以var爲關鍵字存儲到PageContext private String var; @Override public void doTag() throws JspException, IOException { //獲取到集合的迭代器 Iterator iterator = items.iterator(); //獲取到表明標籤體內容的對象 JspFragment jspFragment = this.getJspBody(); //遍歷集合 while (iterator.hasNext()) { Object o = iterator.next(); //把遍歷出來的對象存儲到page範圍中,關鍵字爲標籤的屬性var(在標籤體中使用EL表達式${var},就可以獲取到集合的對象了!) this.getJspContext().setAttribute(var, o); //每設置了一個屬性,我就執行標籤體 jspFragment.invoke(null); } } public void setItems(List items) { this.items = items; } public void setVar(String var) { this.var = var; } }
<tag> <name>forEach</name> <tag-class>tag.Demo1</tag-class> <body-content>scriptless</body-content> <attribute> <name>var</name> <rtexprvalue>true</rtexprvalue> <!--字符串便可,不須要EL表達式的支持--> <required>false</required> </attribute> <attribute> <name>items</name> <required>true</required> <rtexprvalue>true</rtexprvalue> </attribute> </tag>
<% List list = new ArrayList(); list.add("zhongfucneng"); list.add("1"); list.add("2"); list.add("3"); request.setAttribute("list",list); %> <zhongfucheng:forEach items="${list}" var="str"> ${str} </zhongfucheng:forEach>
上面寫的僅僅可以遍歷List集合,作一個通用的forEach標籤麻煩的是在:不知道傳進來的是什麼類型的數組、什麼類型集合!,須要逐一去判斷
//若是items是Collection類型的,就強轉爲Colletion if (items instanceof Collection) { collection = (Collection) items; } //若是itmes是Map類型的,那麼就強轉爲Map,再獲取到<Map.Entry<K, V>,這個是Set集合的! if (items instanceof Map) { Map map = (Map) items; collection = (Collection) map.entrySet(); } //對象數組 if (items instanceof Object[]) { Object[] objects = (Object[]) items; collection = Arrays.asList(objects); } //int[],Byte[],char[]等八大基本數據類型.....
還有int[],byte[],char[]等八大基本數據類型,這八大基本數據類型就不能用Arrays.asList()把引用傳進去了。由於JDK5之後會把引用自動裝箱成Interger[]、Byte[]等等,而不是獲取到數組的元素數據。
public static void main(String[] args) { int[] ints = new int[]{1, 2, 3}; Object[] objects = new Object[]{"1", "2", "3"}; if (objects instanceof Object[]) { Collection collection = Arrays.asList(objects); System.out.println(collection); } if (ints instanceof int[]) { Collection collection1 = Arrays.asList(ints); System.out.println(collection1); } }
if (items instanceof int[]) { int[] ints = (int[]) items; collection = new ArrayList(); for (int anInt : ints) { collection.add(anInt); } } //......這裏還要寫7個
因爲JDK5的新特性,咱們又有另外的解決方案,Class對象可以判斷是否爲數組類,reflect反射包下Array類:
其實,不管Map集合、仍是任何類型的數組、均可以使用Colletion進行遍歷!。
標籤處理器的代碼:
public class Demo1 extends SimpleTagSupport { //遍歷的是未知的集合或數組,定義成Object private Object items; //每次被遍歷的對象存儲關鍵字 private String var; //Colletion private Collection collection; //在WEB容器設置標籤的屬性的時候,判斷是什麼類型的數組和集合 public void setItems(Object items) { this.items = items; //若是items是Collection類型的,就強轉爲Colletion if (items instanceof Collection) { collection = (Collection) items; } //若是itmes是Map類型的,那麼就強轉爲Map,再獲取到<Map.Entry<K, V>,這個是Set集合的! if (items instanceof Map) { Map map = (Map) items; collection = (Collection) map.entrySet(); } //能夠這樣解決,Class對象判斷是不是一個數組類 if (items.getClass().isArray()) { //建立Collection集合添加數組的元素! collection = new ArrayList(); //再利用reflect包下的Array類獲取到該數組類的長度 int len = Array.getLength(items); //遍歷並添加到集合中 for (int i = 0; i < len; i++) { collection.add(Array.get(items, i)); } } } public void setVar(String var) { this.var = var; } @Override public void doTag() throws JspException, IOException { //獲取到表明標籤體內容的對象 JspFragment jspFragment = this.getJspBody(); Iterator iterator = collection.iterator(); //遍歷集合 while (iterator.hasNext()) { Object o = iterator.next(); //把遍歷出來的對象存儲到page範圍中(在標籤體中使用EL表達式${var},就可以獲取到集合的對象了!) this.getJspContext().setAttribute(var, o); jspFragment.invoke(null); } } }
<% /*list集合*/ List list = new ArrayList(); list.add("zhongfucneng"); list.add("1"); list.add("2"); list.add("3"); request.setAttribute("list",list); /*基本數據類型數組*/ int[] ints = new int[]{1, 2, 3, 4, 5}; request.setAttribute("ints", ints); /*對象數組*/ Object[] objects = new Object[]{2, 3, 4, 5, 6}; request.setAttribute("objects", objects); /*map集合*/ Map map = new HashMap(); map.put("aa", "aa"); map.put("bb", "bb"); map.put("cc", "cc"); request.setAttribute("map",map); %> List集合: <zhongfucheng:forEach items="${list}" var="str"> ${str} </zhongfucheng:forEach> <hr> <hr> 基本數據類型數組: <zhongfucheng:forEach items="${ints}" var="i"> ${i} </zhongfucheng:forEach> <hr> <hr> 對象數組: <zhongfucheng:forEach items="${objects}" var="o"> ${o} </zhongfucheng:forEach> <hr> <hr> map集合: <zhongfucheng:forEach items="${map}" var="me"> ${me.key} = ${me.value} </zhongfucheng:forEach>
要開發這個標籤就很簡單了,只要獲取到標籤體的內容,再經過通過方法轉義下標籤體內容,輸出給瀏覽器便可!
public class Demo1 extends SimpleTagSupport { @Override public void doTag() throws JspException, IOException { //獲取到標籤體的內容再修改 StringWriter stringWriter = new StringWriter(); JspFragment jspFragment = this.getJspBody(); jspFragment.invoke(stringWriter); String content = stringWriter.toString(); //通過filter()轉義,該方法在Tomcat能夠找到 content = filter(content); //再把轉義後的內容輸出給瀏覽器 this.getJspContext().getOut().write(content); } private String filter(String message) { if (message == null) return (null); char content[] = new char[message.length()]; message.getChars(0, message.length(), content, 0); StringBuffer result = new StringBuffer(content.length + 50); for (int i = 0; i < content.length; i++) { switch (content[i]) { case '<': result.append("<"); break; case '>': result.append(">"); break; case '&': result.append("&"); break; case '"': result.append("""); break; default: result.append(content[i]); } } return (result.toString()); } }
<zhongfucheng:filter><a href="2.jsp">你好啊</a> </zhongfucheng:filter> <br> <a href="2.jsp">你好啊
在JSTL中並無if else的標籤,JSTL給予咱們的是choose,when,otherwise標籤,如今咱們模仿choose,when,otherwise開發標籤
思路:when標籤有個test屬性,但otherwise怎麼判斷標籤體是執行仍是不執行呢?這時就須要choose標籤的支持了!choose標籤默認定義一個Boolean值爲false,。當when標籤體被執行了,就把Boolean值變成true,只要Boolean值爲false就執行otherwise標籤體的內容。
看程序就容易理解上面那句話了:!
public class Choose extends SimpleTagSupport { private boolean flag; @Override public void doTag() throws JspException, IOException { this.getJspBody().invoke(null); } public boolean isFlag() { return flag; } public void setFlag(boolean flag) { this.flag = flag; } }
public class When extends SimpleTagSupport { private boolean test ; @Override public void doTag() throws JspException, IOException { Choose choose = (Choose) this.getParent(); //若是test爲true和flag爲false,那麼執行該標籤體 if (test == true && choose.isFlag() == false) { this.getJspBody().invoke(null); //修改父標籤的flag choose.setFlag(true); } } public void setTest(boolean test) { this.test = test; } }
public class OtherWise extends SimpleTagSupport { @Override public void doTag() throws JspException, IOException { Choose choose = (Choose) this.getParent(); //若是父標籤的flag爲false,就執行標籤體(若是when標籤沒執行,flag值就不會被修改!when標籤沒執行,就應該執行otherwise標籤!) if (choose.isFlag() == false) { getJspBody().invoke(null); //改父標籤的flag爲false choose.setFlag(true); } } }
<zhongfucheng:choose> <zhongfucheng:when test="${user!=null}"> user爲空 </zhongfucheng:when> <zhongfucheng:otherwise> user不爲空 </zhongfucheng:otherwise> </zhongfucheng:choose>
此接口的主要功能是用於完成動態屬性的設置!前面咱們講解屬性標籤的時候,屬性都是寫多少個,用多少個的。如今若是我但願屬性能夠動態的增長,只須要在標籤處理器類中實現DynamicAttribute接口便可!
如今我要開發一個動態加法的標籤:
public class Demo1 extends SimpleTagSupport implements DynamicAttributes { //既然有動態屬性和動態的值,那麼咱們就用一個Map集合存儲(1-1對應的關係),作的加法運算,值爲Double類型的。 Map<String, Double> map = new HashMap<>(); @Override public void doTag() throws JspException, IOException { //定義一個sum變量用於計算總值 double sum = 0.0; //獲取到Map集合的數據 Iterator iterator = map.entrySet().iterator(); while (iterator.hasNext()) { Map.Entry<String, Double> entry = (Map.Entry<String, Double>) iterator.next(); sum += entry.getValue(); } //向瀏覽器輸出總和是多少 this.getJspContext().getOut().write(String.valueOf(sum)); } //對於這個要實現的方法,咱們只要關注第2個參數和第3個參數便可 //第二個參數表示的是動態屬性的名稱,第三個參數表示的是動態屬性的值 @Override public void setDynamicAttribute(String s, String localName, Object value) throws JspException { //將動態屬性的名字和值加到Map集合中 map.put(localName, Double.valueOf(Float.valueOf(value.toString()))); } }
<tag> <name>dynamicAttribute</name> <tag-class> tag.Demo1</tag-class> <body-content> empty</body-content> <!--這個必需要設置爲true--> <dynamic-attributes>true</dynamic-attributes> </tag>
<zhongfucheng:dynamicAttribute num="1.1" num2="2.2" num3="1"/>
至於怎麼開發自定義函數,在EL表達式的博客中有!