[Day 27] 自订标签 – classic Tag

前言

今天来看到classic tag
这部分较复杂一点点


Classic Tag简介

最早JSP所提供的自订标标签处理器
在JSP 2.0之后虽然simple tag结合JSTL和tag file已经可以处理大部分的事了
但这块还是可以稍微了解一下
且若要考认证考试还是会出个几题

Classic Tag架构

有别于simple tag的doTag
classic主要分成两个部分
doStartTag()和doEndTag()
在JSP和TLD和simplet tag是相同的(也就是说TLD也可以切换到呼叫simple tag)
主要差在java程序,上述的两个方法已以及回传的参数意义
还有IOException要另外处理的部分

ClassiclTag.java

package com.web.tag;

import java.io.IOException;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.TagSupport;
public class ClassiclTag extends TagSupport{
	JspWriter output;
	// Classic并没有宣告IOException,所以要在程序里面自己try catch
	public int doStartTag() throws JspException{
		//从TagSupport继承到pageContext(类似simple tag的getJspContext())
		output = pageContext.getOut();
		try{
			output.print("doStartTag...");
		}catch(IOException ex)
		{
			throw new JspException("IOException is "+ex.toString());
		}
		//这个参数表示就算有body,也直接跳到doEndTag方法
        //若要执行body(具有主体时),则回传EVAL_BODY_INCLUDE
		return SKIP_BODY;
		
	}
	public int doEndTag()throws JspException{
		try{
			output.print("doEndTag...");
		}catch(IOException ex)
		{
			throw new JspException("IOException is "+ex.toString());
		}
		//继续执行剩下的部分
		//和它相反的为SKIP_PAGE,类似simple的SkipPageException
		return EVAL_PAGE;
	}
}

Classic Tag生命周期

因为Classic有doStartTag和doEndTag
不像simple tag有dotag()一次做完
所以classic提供了doAfterBody()来处理body的部分

顺序如下:

  • 1.载入Classic类别,建立instance(tag 的clasee 为无参数的Constructor)
  • 2.呼叫setPageContext(PageContext)去设定PageContext
  • 3.如果此标签为巢状(即被包在另一个标签内),则呼叫setParent(Tag)
  • 4.若设定标签有属性,则呼叫设定属性的方法(使用javaBean命名方式)
  • 5.呼叫doStartTag(),只会被呼叫一次
    若tag设定body不为空,且body也有内容,则回传EVAL_BODY_INCLUDE
    若为空则回传SKIP_BODY
  • 6.若执行完body则呼叫doAfterBody(),在执行body之后呼叫,可不只被呼叫一次
  • 7.呼叫doEndTag(),只会被呼叫一次

整个流程及回传的return值直接用下图表示
其中所有的回传值只有EVAL_BODY_AGAIN是由IterationTag所提供
http://ithelp.ithome.com.tw/upload/images/20161227/20103425x83xsbeFVm.jpg

使用IterationTag处理body

刚刚有提到的doAfterBody()就是由IterationTag提供
IterationTag API

若在doAfterBody()里面set属性
因为doAfterBody()是在主体被执行一次才会执行
所以在doStartTag()时也需要set一次相同的属性,否则在doStartTag()时会找不到此属性
画面上会多一个空值

另外Classic Tag是可以重复被Container使用
这点和Simple tag不相同,因此在参数的设定上也需要注意是否要重新设定其值

存取Body

Simple tag可以利用writer的方法,处理body的内容
但classic tag
则需要BodyTag来帮忙
BodyTag继承了BodyTagSupport
可以使用setBodyContext()和doInitBody()
来呼叫处理器的实际主内容去做控制

因此整个流程就变成,多了两个方法和一个回传值
http://ithelp.ithome.com.tw/upload/images/20161227/201034251rA6OTk548.jpg

巢状标签

这里分成两个部分讨论(此部分simple 和classic都相同)

  • 1.父tag要取得子tag的资讯 => 新增属性
  • 2.子tag要取得父tag的资讯 => getParent()

1.父tag要取得子tag的资讯
因为没有提供往下找的方法
所以需要在子tag里面去新增属性,将子tag的属性传给父tag

ParentTag.java

package com.web.tag;
import java.util.ArrayList;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.TagSupport;
public class ParentTag extends TagSupport{
	
	private ArrayList arrayName;
	
	//新增一个方法供子tag呼叫
	public void addItem(String s)
	{
		arrayName.add(s);
	}
	
	public int doStartTag() throws JspException{
		return EVAL_BODY_INCLUDE;
	}
	public int doEndTag()throws JspException{
		return EVAL_PAGE;
	}
}

SubTag.java

package com.web.tag;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.TagSupport;
public class SubTag extends TagSupport{
	private String str;
	
	public void setStr(String str) {
		this.str = str;
	}
	public int doStartTag() throws JspException{
		return EVAL_BODY_INCLUDE;
	}
	public int doEndTag()throws JspException{
		//呼叫父tag,使用addItem将子tag的值传入
		ParentTag parTag = (ParentTag) getParent();
		parTag.addItem(str);
		return EVAL_PAGE;
	}
}

2.子tag要取得父tag的资讯
不管是simple和tag都具有getParent()的方法
只是回传的型别不相同
因此可以在子tag里面使用getParent()得到父tag的物件
但要记得做转型

此外由于simple回传是JspTag
包含了tag的interface
所以getParent()

classic tag只能存取classic tag的父标签
但simple tag除了存取自己类型的父tag之外,也可存取classic tag


小结

其实今天还有漏掉一个主题
DynamicAttributes
不是因为偷懒,是因为还需要花一些时间研究一下
不是很了解书中讲解的范例
明天会先继续后面的进度
再找时间练习这个部分,之后再补充上来