码迷,mamicode.com
首页 > 其他好文 > 详细

如何编写自定义标签?具体流程与分析(自定义标签快速入门)

时间:2015-05-18 23:11:15      阅读:256      评论:0      收藏:0      [点我收藏+]

标签:自定义标签库   标签   标签库   tag   tagsupport   

1.自定义标签简介


自定义标签主要用于移除Jsp页面中的java代码
使用自定义标签移除jsp页面中的java代码,只需要完成以下两个步骤
1.编写一个实现Tag接口的Java类(标签处理器类)
2.在WEB-INF中编写标签库描述符(tld)文件,在tld文件中对标签处理器类描述成一个标签
(参考tomcat中的examples 项目中jsp部分)

2.自定义标签的执行流程的分析


JSP引擎将遇到自定义标签时,首先创建标签处理器类的实例对象,然后按照JSP规范定义的通信规则依次调用它的方法。

  1、public void setPageContext(PageContext pc),JSP引擎实例化标签处理器后,将调用setPageContext方法将JSP页面的pageContext对象传递给标签处理器,标签处理器以后可以通过这个pageContext对象与JSP页面进行通信。

  2、public void setParent(Tag t),setPageContext方法执行完后,WEB容器接着调用的setParent方法将当前标签的父标签传递给当前标签处理器,如果当前标签没有父标签,则传递给setParent方法的参数值为null。

  3、public int doStartTag(),调用了setPageContext方法和setParent方法之后,WEB容器执行到自定义标签的开始标记时,就会调用标签处理器的doStartTag方法。

  4、public int doEndTag(),WEB容器执行完自定义标签的标签体后,就会接着去执行自定义标签的结束标记,此时,WEB容器会去调用标签处理器的doEndTag方法。

  5、public void release(),通常WEB容器执行完自定义标签后,标签处理器会驻留在内存中,为其它请求服务器,直至停止web应用时,web容器才会调用release方法。


技术分享

3.自定义标签体可以实现的功能


开发人员在编写Jsp页面时,经常还需要在页面中引入一些逻辑,例如:
控制jsp页面某一部分内容是否执行。
控制整个jsp页面是否执行。
控制jsp页面内容重复执行。
修改jsp页面内容输出。

自定义标签除了可以移除jsp页面java代码外,它也可以实现以上功能。


4.tld文件中的四种标签体类型


EMPTY     表示标签没有标签体
JSP     表示标签体可以包含JSP代码
scriptless   表示标签体可以包含EL表达式和JSP的动作元素,但不能包含脚本表达式
tagdepentend    表示标签体由标签本身去解析处理。若指定tagdependent,那么在标签体中所写的代码将作为纯文本原封不动地传给标签处理类,     而不是将执行结果传给标签处理类

5.案例示例

(1)控制标签体内容是否执行

doStartTag
EVAL_BODY_INCLUDE  执行标签内容
SKIP_BODY  跳过标签内容
Tld配置
<tag>
<name>demo1</name>
<tag-class>mytag.MyTag1</tag-class>
<body-content>JSP</body-content>
</tag>
public class Demo1 extends TagSupport {
	// 如何控制是否执行
	// EVAL_BODY_INCLUDE 想执行标签体
	// SKIP_BODY 不想执行标签体
	@Override
	public int doStartTag() throws JspException {
		// TODO Auto-generated method stub
		return SKIP_BODY;
	}

(2)控制标签后的jsp页面是否执行

doEndTag
EVAL_PAGE  执行标签后的页面内容
SKIP_PAGE  跳过标签后的页面内容
Tld配置
<tag>
<name>demo2</name>
<tag-class>mytag.MyTag2</tag-class>
<body-content>empty</body-content>
       </tag>

public class Demo2 extends TagSupport {
	// 控制标签后内容
	// EVAL_PAGE 执行后面内容
	// SKIP_PAGE 不执行后面内容
	@Override
	public int doEndTag() throws JspException {
		return SKIP_PAGE;
	}
}

(3)控制jsp页面内容重复执行

doStartTag
EVAL_BODY_INCLUDE  执行标签内容
doAfterBody
EVAL_BODY_AGAIN  重复执行标签内容
SKIP_BODY  跳过标签内容

doAfterBody代码

times--;

if (times > 0) {

    return EVAL_BODY_AGAIN;

}else {

return SKIP_BODY;

}

public class Demo3 extends TagSupport {
	private int times = 10;

	// 执行标签体内容
	@Override
	public int doStartTag() throws JspException {
		return EVAL_BODY_INCLUDE;
	}

	// 重复标签体
	// EVAL_BODY_AGAIN 重复执行标签体
	// SKIP_BODY 继续执行 ---- doEndTag
	@Override
	public int doAfterBody() throws JspException {
		if (times > 1) {
			times--;
			return EVAL_BODY_AGAIN;
		} else {
			times = 10;
			return SKIP_BODY;
		}
	}
}

(4)修改jsp页面内容输出

将标签体内容大写输出
MyTag4 extends BodyTagSupport
doStartTag
EVAL_BODY_BUFFERED
doEndTag
Stringcontent = getBodyContent().getString();
pageContext.getOut().write(…);
public class Demo4 extends BodyTagSupport {
	// 改变标签体内容
	// 将标签体内容输出到缓存中
	@Override
	public int doStartTag() throws JspException {
		return EVAL_BODY_BUFFERED;
	}

	// 取出缓存中标签体内容
	// 转换成大写输出
	@Override
	public int doEndTag() throws JspException {
		// 从缓存取出标签体内容
		BodyContent bodyContent = getBodyContent();
		String content = bodyContent.getString();

		content = content.toUpperCase();

		// 输出
		try {
			pageContext.getOut().println(content);
		} catch (IOException e) {
			e.printStackTrace();
		}

		return super.doEndTag();
	}
}

6.开发带属性的标签

自定义标签可以定义一个或多个属性,这样,在JSP页面中应用自定义标签时就可以设置这些属性的值,通过这些属性为标签处理器传递参数信息,从而提高标签的灵活性 和复用性。

要想让一个自定义标签具有属性,通常需要完成两个任务:
?在标签处理器中编写每个属性对应的setter方法
?在TLD文件中描术标签的属性

为自定义标签定义属性时,每个属性都必须按照JavaBean的属性命名方式,在标签处理器中定义属性名对应的setter方法,用来接收JSP页面调用自定义标签时传递进来的 属性值。例如属性url,在标签处理器类中就要定义相应的setUrl(Stringurl)方法。
在标签处理器中定义相应的set方法后,JSP引擎在解析执行开始标签前,也就是调用doStartTag方法前,会调用set属性方法,为标签设置属性


在TLD中描述标签属性attribute:

元素名

是否必须指定

描      述

description

用于指定属性的描述信息。

name

用于指定属性的名称。属性名称是大小写敏感的,并且不能以jsp_jspjavasun开头。

required

用于指定在JSP页面中调用自定义标签时是否必须设置这个属性。其取值包括truefalse,默认值为falsetrue表示必须设置,否则可以设置也可以不设置该属性。

rtexprvalue

rtexprvalueruntime expression value(运行时表达式)的英文简写,用于指定属性值是一个静态值或动态值。其取值包括truefalse,默认值为falsefalse表示只能为该属性指定静态文本值,例如"123"true表示可以为该属性指定一个JSP动态元素,动态元素的结果作为属性值,例如JSP表达式<%=value %>

type

用于指定属性值的Java类型。


案例示例:

控制jsp页面内容重复执行(属性控制循环次数)
通过成员变量和 setter方法设置属性  times
Tld配置
<tag>
… ...
<attribute>
<name>times</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
     </tag> 
public class Demo5 extends TagSupport {
	private int times; // 一定注意:成员变量名称要和属性一致

	// 必须存在set方法
	public void setTimes(int times) {
		this.times = times;
	}
	
	// 先执行 setTimes 再执行 doStartTag
	@Override
	public int doStartTag() throws JspException {
		return EVAL_BODY_INCLUDE;
	}

	@Override
	public int doAfterBody() throws JspException {
		if (times > 1) {
			times--;
			return EVAL_BODY_AGAIN;
		} else {
			return SKIP_BODY;
		}
	}
}

7.简单标签(SimpleTag)开发

由于传统标签使用三个标签接口来完成不同的功能,显得过于繁琐,不利于标签技术的推广,SUN公司为降低标签技术的学习难度,在JSP 2.0中定义了一个更为简单、便 于编写和调用的SimpleTag接口来实现标签的功能。实现SimpleTag接口的标签通常称为简单标签。简单标签共定义了5个方法:
setJspContext方法
setParent和getParent方法
setJspBody方法
doTag方法

setJspContext方法
?用于把JSP页面的pageContext对象传递给标签处理器对象
setParent方法
?用于把父标签处理器对象传递给当前标签处理器对象
getParent方法
?用于获得当前标签的父标签处理器对象
setJspBody方法
?用于把代表标签体的JspFragment对象传递给标签处理器对象
doTag方法
?用于完成所有的标签逻辑,包括输出、迭代、修改标签体内容等。在doTag方法中可以抛出javax.servlet.jsp.SkipPageException异常,用于通知WEB容器不再执行JSP页  面中位于结束标记后面的内容,这等效于在传统标签的doEndTag方法中返回Tag.SKIP_PAGE常量的情况。 

SimpleTag接口方法的执行顺序:
当web容器开始执行标签时,会调用如下方法完成标签的初始化
?WEB容器调用标签处理器对象的setJspContext方法,将代表JSP页面的pageContext对象传递给标签处理器对象。
?WEB容器调用标签处理器对象的setParent方法,将父标签处理器对象传递给这个标签处理器对象。注意,只有在标签存在父标签的情况下,WEB容器才会调用这个方 法。
?如果调用标签时设置了属性,容器将调用每个属性对应的setter方法把属性值传递给标签处理器对象。如果标签的属性值是EL表达式或脚本表达式,则WEB容器首先计算 表达式的值,然后把值传递给标签处理器对象。
?如果简单标签有标签体,容器将调用setJspBody方法把代表标签体的JspFragment对象传递进来。
执行标签时:
?容器调用标签处理器的doTag()方法,开发人员在方法体内通过操作JspFragment对象,就可以实现是否执行、迭代、修改标签体的目的。


JspFragment类:
javax.servlet.jsp.tagext.JspFragment类是在JSP2.0中定义的,它的实例对象代表JSP页面中的一段符合JSP语法规范的JSP片段,这段JSP片段中不能包含JSP脚本元素。

WEB容器在处理简单标签的标签体时,会把标签体内容用一个JspFragment对象表示,并调用标签处理器对象的setJspBody方法把JspFragment对象传递给标签处理器对 象。JspFragment类中只定义了两个方法,如下所示:

getJspContext方法
?用于返回代表调用页面的JspContext对象.

publicabstract void invoke(java.io.Writer out)
?用于执行JspFragment对象所代表的JSP代码片段
?参数out用于指定将JspFragment对象的执行结果写入到哪个输出流对象中,如果传递给参数out的值为null,则将执行结果写入到JspContext.getOut()方法返回的 输出流对象中。(简而言之,可以理解为写给浏览器)

invoke方法详解 
JspFragment.invoke方法是JspFragment最重要的方法,利用这个方法可以控制是否执行和输出标签体的内容、是否迭代执行标签体的内容或对标签体的执 行结果进行修改后再输出。例如:
?在标签处理器中如果没有调用JspFragment.invoke方法,其结果就相当于忽略标签体内容;
?在标签处理器中重复调用JspFragment.invoke方法,则标签体内容将会被重复执行;
?若想在标签处理器中修改标签体内容,只需在调用invoke方法时指定一个可取出结果数据的输出流对象(例如StringWriter),让标签体的执行结果输出 到该输出流对象中,然后从该输出流对象中取出数据进行修改后再输出到目标设备,即可达到修改标签体的目的。

案例示例:

一、控制标签体内容是否执行

doTag
JspFragment jspFragment = getJspBody();
jspFragment.invoke(getJspContext().getOut());
jspFragment.invoke(null);

Tld配置
<tag>
<name>demo1</name>
<tag-class>simple.MyTag1</tag-class>
<body-content>scriptless</body-content>
</tag>
public class Demo1 extends SimpleTagSupport {
	@Override
	public void doTag() throws JspException, IOException {
		// 不执行 什么都不用写

		// 执行
		// JspFragment fragment = getJspBody(); // 获得标签体内容对象
		// 输出页面上

		// 获得 pageContext
		// PageContext pageContext = (PageContext) getJspContext();
		// fragment.invoke(pageContext.getOut());

		// 简化代码
		getJspBody().invoke(null); // 默认写入out流
	}
}


二、控制标签后的jsp页面是否执行

doTag
throw new SkipPageException()

Tld配置
<tag>
<name>demo2</name>
<tag-class>simple.MyTag2</tag-class>
<body-content>empty</body-content>
</tag>
public class Demo2 extends SimpleTagSupport {
	@Override
	public void doTag() throws JspException, IOException {
		// 剩余页面内容想执行 什么也不用写

		// 如果你想剩下页面部分不执行
		throw new SkipPageException();
	}
}

三、控制jsp页面内容重复执行

doTag
JspFragmentfragment = getJspBody();
for (int i = 0;i <times; i++) {
 fragment.invoke(null);
}

Tld配置
<tag>
<name>demo3</name>
<tag-class>simple.MyTag3</tag-class>
<body-content>scriptless</body-content>
</tag>

public class Demo3 extends SimpleTagSupport {
	@Override
	public void doTag() throws JspException, IOException {
		for (int i = 0; i < 3; i++) {
			getJspBody().invoke(null);
		}
	}
}

四、修改jsp页面内容输出

将标签体内容大写输出
doTag
JspFragmentfragment = getJspBody();
StringWriterwriter = new StringWriter();
fragment.invoke(writer);
getJspContext().getOut().println(
writer.getBuffer().toString().toUpperCase());
public class Demo4 extends SimpleTagSupport {

	private int times; // 配置tld文件

	@Override
	public void doTag() throws JspException, IOException {
		// 获得标签体对象
		JspFragment fragment = getJspBody();
		// 只能调用invoke方法 将标签体内容输出到 指定输出流
		StringWriter buffer = new StringWriter(); // 输出流,流方向String缓冲区里
		fragment.invoke(buffer); // 将标签体内容写入缓存

		// 获得内容
		String content = buffer.toString();
		// 转换大写
		content = content.toUpperCase();

		// 输出页面
		PageContext pageContext = (PageContext) getJspContext();
		pageContext.getOut().println(content);
	}

	public void setTimes(int times) {
		this.times = times;
	}

}

8.综合案例

一、自定义实现HTML转义标签

package cn.demo;

import java.io.IOException;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.BodyTagSupport;

public class HTMLTag extends BodyTagSupport {
	@Override
	public int doStartTag() throws JspException {
		return EVAL_BODY_BUFFERED; // 将 标签体内容存入 缓存里
	}

	@Override
	public int doEndTag() throws JspException {
		// 从缓存 获得标签体内容
		String content = getBodyContent().getString();
		content = filter(content);

		// 输出内容
		JspWriter out = pageContext.getOut();
		try {
			out.println(content);
		} catch (IOException e) {
			e.printStackTrace();
		}

		// 控制标签后页面内容是否继续执行
		return EVAL_PAGE;
	}

	public static 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());

	}
}
tld配置:
<tag>
  <name>html</name>
  <tag-class>cn.itcast.demo.HTMLTag</tag-class>
  <body-content>scriptless</body-content>
 </tag>


二、自定义防盗链标签

package cn.demo;

import java.io.IOException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.TagSupport;

// 防止盗链 本质控制标签后页面是否继续执行 demo2
public class RefererTag extends TagSupport {
	@Override
	public int doEndTag() throws JspException {
		// 获得request对象和out对象
		HttpServletRequest request = (HttpServletRequest) pageContext
				.getRequest();
		JspWriter out = pageContext.getOut();
		// 判断referer
		String referer = request.getHeader("referer");
		// 什么情况是盗链 referer == null || referer不是以当前资源网站路径开始
		String currentURL = request.getRequestURL().toString();
		String currentURI = request.getRequestURI();
		String currentPath = currentURI.substring(request.getContextPath()
				.length());

		// URL截掉currentPath
		String prefix = currentURL.substring(0, currentURL
				.lastIndexOf(currentPath));

		if (referer == null || !referer.startsWith(prefix)) {
			// 盗链
			try {
				out.println("你当前的请求是盗链!");
			} catch (IOException e) {
				e.printStackTrace();
			}
			// 接下来页面不需要执行
			return SKIP_PAGE;
		} else {
			// 不是盗链,执行接下来页面
			return EVAL_PAGE;
		}
	}
}
tld配置:
<tag>
  <name>referer</name>
  <tag-class>cn.itcast.demo.RefererTag</tag-class>
  <body-content>empty</body-content>
 </tag>


9.自定义标签库的打包与导入


当编写了很多自定义标签之后,我们需要将其打包成一个jar包,以备以后或者他人使用。
打包步骤(myeclipse):
打包标签库 ---- class文件 
1、新建一个java工程
2、将定义好标签类文件拷贝到java工程src下 ----- 解决报错问题(导入javaee6.0或以上的编译环境,在addlibrary导入) (java文件没有错误----编译class)
3、在java工程里新建 META-INF目录,将定义好的tld文件拷贝到META-INF
4、使用工具导出 ---- jar文件 (右击工程文件包----Export----jar文件)

导入方法:直接把jar包导入web工程的WEB-INF下的lib文件夹 即可在jsp中导入使用 (<%@taglib url="xxx" prefix="xxx" %> )












~~~~以上由本人书写整理,转载请注明出处~~~~~

如何编写自定义标签?具体流程与分析(自定义标签快速入门)

标签:自定义标签库   标签   标签库   tag   tagsupport   

原文地址:http://blog.csdn.net/qhwc2009/article/details/45825293

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!