概述
TLD文件实现自定义标签
之前我们使用的标签库都是JSTL为我们提供的,大部分的数据操作和控制都可以使用它来完成,但是如果我们项目中有特殊需求或者为了统一开发规范,那么我们也可以自己定义一套标签库供自己的团队使用。通过实现java提供的JspTag的子接口编写标签处理类,完成自定义标签的定义。
实现步骤:
- 创建标签处理程序 (Tag Handler Class)
- 创建标签库描述文件(Tag Library Descrptor File)
在web.xml文件中配置元素(可选,如果标签库描述文件不放在WEB-INF下则需要配置)
<jsp-config> <taglib> <!-- tld描述文件中的uri --> <taglib-uri></taglib-uri> <!-- 存放路径 --> <taglib-location></taglib-location> </taglib> </jsp-config>
在JSP文件中使用taglib指令引入标签库
- 使用标准格式调用自定义标签
JspTag标签分为简单的标签和传统的标签,树形图如下:
简单示例
示例
标签实现类
package com.yt.tag
public class IpTag implements Tag {
private PageContext pageContext;
/**
* 设置pageContext
*/
@Override
public void setPageContext(PageContext pc) {
this.pageContext = pc;
}
/**
* 设置父类标签
*/
@Override
public void setParent(Tag t) {
}
/**
* 获取父类标签
*/
@Override
public Tag getParent() {
return null;
}
/**
* 开始标签,一定会被调用
*/
@Override
public int doStartTag() throws JspException {
HttpServletRequest request = (HttpServletRequest)pageContext.getRequest(); //获取request
JspWriter out = pageContext.getOut(); //获取out</span>
String ip = request.getRemoteAddr(); //通过request获取客户机的ip
try {
out.write(ip); //写到浏览器
} catch (IOException e) {
throw new RuntimeException(e);
}
return 0;
}
/**
* 结束标签
*/
@Override
public int doEndTag() throws JspException {
return 0;
}
/**
* 释放标签
*/
@Override
public void release() {
}
}
创建标签库描述文件(TLD) myTag.tld
<?xml version="1.0" encoding="UTF-8" ?>
<taglib xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"
version="2.0">
<description>A tag library exercising SimpleTag handlers</description>
<tlib-version>1.0</tlib-version>
<short-name>my</short-name> <!-- 定义一个短名 -->
<uri>/WEB-INF/static/tld</uri> <!-- 定义被jsp页面引用的uri -->
<tag>
<name>ip</name>
<tag-class>com.yt.tag.IpTag</tag-class> <!-- 标签实现类 -->
<body-content>empty</body-content> <!--标签体为空-->
</tag>
</taglib>
jsp页面使用自定义标签
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="m" uri="/WEB-INF/static/tld" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>MyTag</title>
</head>
<body>
<m:ip/>
</body>
</html>
上述自定义标签执行过程:
- tomcat服务器启动时,加载到每个web应用,加载每个web应用的WEB-INF目录下的所有文件;
- JSP引擎首先通过uri和ip标签名去找tld文件,在tld中通过ip找到IpTag类;
- IpTag类首先调用 setPageContext 方法把页面的 pageContext 传递进来;
- 再调用setParent把父标签传递进来(没有则不执行),至此完成了标签的初始化工作;
- 然后调用doStartTag和doEndTag方法,开始和结束标签;
- 最后调用release方法释放标签,运行时所占的资源。
标签体
上述简单示例中标签体设置为empty,<body-content>元素的可选值有:
- empty:无标签体。
- JSP:传统标签支持它,SimpleTag已经不再支持使用<body-content>JSP</body-content>;标签体内容可以是任何东西:EL、JSTL、<%=%>、<%%>,以及html;
- scriptless:标签体内容不能是Java脚本,但可以是EL、JSTL等。SimpleTag中需要标签体时使用它。
- tagdependent:标签体内容不做运算,由标签处理类自行处理,无论标签体内容是EL、JSP、JSTL,都不会做运算。这个选项几乎没有人会使用。
传统标签
Tag接口
public interface Tag extends JspTag {
public final static int SKIP_BODY = 0;
public final static int EVAL_BODY_INCLUDE = 1;
public final static int SKIP_PAGE = 5;
public final static int EVAL_PAGE = 6;
void setPageContext(PageContext pc);
void setParent(Tag t);
Tag getParent();
int doStartTag() throws JspException;
int doEndTag() throws JspException;
void release();
}
tag接口的执行过程:
验证上述执行过程:
public class TimeTag implements Tag{
private PageContext pageContext;
private Tag parent;
//标签属性
private String color;
public TimeTag() {
super();
System.out.println("实例化TimeTag");
}
@Override
public void setPageContext(PageContext pc) {
this.pageContext = pc;
System.out.println("设置pageContext....");
}
@Override
public void setParent(Tag t) {
this.parent = t;
System.out.println("设置parent.....");
}
@Override
public Tag getParent() {
return this.parent;
}
@Override
public int doStartTag() throws JspException {
System.out.println("doStartTag............");
return Tag.SKIP_BODY;
}
@Override
public int doEndTag() throws JspException {
System.out.println("doEndTag................");
Date date=new Date();
SimpleDateFormat df=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String re=df.format(date);
try {
//这里用PageContext的对象的getOut()方法(这样就能在页面中输出了)。
pageContext.getOut().println("<h1 style='color:"+this.color+";'>"+re+"</h1>");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return Tag.EVAL_PAGE;
}
@Override
public void release() {
System.out.println("release..........");
}
public String getColor() {
return color;
}
public void setColor(String color) {
System.out.println("setColor");
this.color = color;
}
}
<tag>
<name>time</name>
<tag-class>com.yt.tag.TimeTag</tag-class>
<body-content>empty</body-content>
<!--标签属性的信息 如果有属性,在标签实现类中set/get-->
<attribute>
<name>color</name>
<required>true</required>
<!--表示可以出来JSP表达式-->
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
<m:time color="red"/>
Tag接口定义了4个静态常量:
- SKIP_BODY:忽略标签体中的内容(作用在doStartTag方法中)
- EVAL_BODY_INCLUDE:将标签体的内容进行输出,等同于SimpleTag中JspFragment的invoke(null)(但是获取不了)
- SKIP_PAGE:等同于new SkipPageException(),不执行JSP页面标签后面的内容(作用在doEndTag方法中)
- EVAL_PAGE:执行JSP页面标签后面的内容
带标签体的Tag
tld描述文件中的body-content可以是scriptless或JSP
<body-content>scriptless</body-content> <!--可以是JSP-->
TimeTag类中改变doStartTag的返回值
@Override
public int doStartTag() throws JspException {
System.out.println("doStartTag............");
return Tag.EVAL_BODY_INCLUDE;
}
<m:time color="red">${param.name}</m:time>
标签体内容可以是El、JSTL、HTML标签和纯文本等,标签实现类会自动解析内容输出到浏览器。如上述可以在请求url后加上?name=红色。
IterationTag接口
这个接口是Tag接口的子接口,它可以实现body的循环。
public interface IterationTag extends Tag {
public final static int EVAL_BODY_AGAIN = 2;
int doAfterBody() throws JspException;
}
上述接口定义了一个新的静态常量EVAL_BODY_AGAIN和方法doAfterBody。
EVAL_BODY_AGAIN:重新执行 (作用在doAfterBody方法中)
IterationTag接口的执行过程:
以下模拟一个类似于<c:forEache>的循环迭代
<%
List list = new ArrayList();
list.add("a");
list.add("b");
list.add("c");
list.add("d");
pageContext.setAttribute("list", list);
%>
<m:for item="${list}" var="i">
${i}
</m:for>
<tag>
<name>for</name>
<tag-class>com.yt.tag.ForTag</tag-class>
<body-content>scriptless</body-content>
<attribute>
<name>item</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>var</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
public class ForTag implements IterationTag{
private PageContext pageContext;
private Tag parent;
private List item; //需要迭代的List集合
private String var; //List集合元素值的变量
private int index=0; //索引
public List getItem() {
return item;
}
public void setItem(List item) {
this.item = item;
}
public String getVar() {
return var;
}
public void setVar(String var) {
this.var = var;
}
public PageContext getPageContext() {
return pageContext;
}
@Override
public void setPageContext(PageContext pc) {
this.pageContext = pc;
}
@Override
public void setParent(Tag t) {
this.parent = t;
}
@Override
public Tag getParent() {
return this.parent;
}
@Override
public int doStartTag() throws JspException {
pageContext.setAttribute(var, item.get(index));//这里设置的属性只会执行一次,被输出到浏览器
return Tag.EVAL_BODY_INCLUDE;
}
@Override
public int doEndTag() throws JspException {
return Tag.EVAL_PAGE;
}
@Override
public void release() {
}
@Override
public int doAfterBody() throws JspException {
if(++index<item.size()){
pageContext.setAttribute(var, item.get(index)); //循环执行
return IterationTag.EVAL_BODY_AGAIN; //循环输出标签体到当前输出流
}
return Tag.SKIP_BODY;
}
}
BodyTag接口
这个接口是IterationTag的子接口,上述标签都不可以读body的值,该标签实现了对body部分的读写。
public interface BodyTag extends IterationTag {
public final static int EVAL_BODY_TAG = 2; //已过时
public final static int EVAL_BODY_BUFFERED = 2;
void setBodyContent(BodyContent b);
void doInitBody() throws JspException;
EVAL_BODY_BUFFERED :把标签主体内容放入缓存器中不要输出(作用在主体标签中),让BodyTag调用setBodyContent获取BodyContent。
BodyTag接口的执行过程:
<m:body>
我是一名java程序员
</m:body>
<tag>
<name>body</name>
<tag-class>com.yt.tag.BodyContentTag</tag-class>
<body-content>scriptless</body-content>
</tag>
public class BodyContentTag implements BodyTag{
private BodyContent bodyContent;
private PageContext pageContext;
private Tag parent;
@Override
public int doAfterBody() throws JspException {
return Tag.SKIP_BODY;
}
@Override
public void setPageContext(PageContext pc) {
this.pageContext = pc;
}
@Override
public void setParent(Tag t) {
this.parent = t;
}
@Override
public Tag getParent() {
return this.parent;
}
@Override
public int doStartTag() throws JspException {
return BodyTag.EVAL_BODY_BUFFERED; //接下来会依次执行setBodyContent、doInitBody()和doAfterBody()
}
@Override
public int doEndTag() throws JspException {
StringBuilder sb = new StringBuilder();
String msg = bodyContent.getString();
String newmsg = msg.replace("java", "C#");
sb.append("<div style='width:200px;height:200px;border:1px #ccc solid; line-height:200px;text-align:center;'>");
sb.append(newmsg);
sb.append("<div");
try {
pageContext.getOut().print(sb.toString());
} catch (IOException e) {
e.printStackTrace();
}
return Tag.EVAL_PAGE;
}
@Override
public void release() {
}
@Override
public void setBodyContent(BodyContent b) {
this.bodyContent = b;
}
@Override
public void doInitBody() throws JspException {
}
}
BodyTagSupport类
该类实现了BodyTag, IterationTag, JspTag, Tag这些接口,并且有一个常量:bodyContent,是BodyContent类实例化的对象,已经实例化好的,可以直接用。
这个类的实现和我们上述自定义的实现类BodyContentTag 基本一致。
TagSupport类
这个类是IterationTag的实现类,和BodyTagSupport比较类似。里面封装的常量不同。区别在于二者定义的属性不同:
BodyTagSupport定义的属性
protected BodyContent bodyContent;
TagSupport定义的属性
private Tag parent;
private Hashtable<String, Object> values;
protected String id;
protected PageContext pageContext;
简单标签
SimpleTag接口:
在jsp2.0中也新增加的接口,可以实现它来做制作标签处理类,而不用处理一些TagSupport、BodyTagSupport类中来回传值的问题。
public interface SimpleTag extends JspTag {
//标签执行方法
public void doTag() throws javax.servlet.jsp.JspException, java.io.IOException;
//设置父标签
public void setParent( JspTag parent );
//获取父标签
public JspTag getParent();
//设置pageContext
public void setJspContext( JspContext pc );
//设置标签体对象
public void setJspBody( JspFragment jspBody );
}
下面同样实现与<c:forEach>类似的迭代功能,与前面实现IterationTag的类ForTag做比较,进而看出实现SimpleTag接口的简便
public class SimpleForTag implements SimpleTag{
private JspContext jspContext; //pageContext父类,都是抽象类,因此基本没区别
private JspTag parent;
private JspFragment jspBody;
private List item;
private String var;
@Override
public void doTag() throws JspException, IOException {
JspWriter out = this.getJspContext().getOut();
//循环遍历标签体
for(Iterator it = item.iterator();it.hasNext();){
String name = (String) it.next();
this.getJspContext().setAttribute(var, name);
this.getJspBody().invoke(null); //body-content为scriptless
}
}
public List getItem() {
return item;
}
public void setItem(List item) {
this.item = item;
}
public String getVar() {
return var;
}
public void setVar(String var) {
this.var = var;
}
public JspContext getJspContext() {
return jspContext;
}
public JspFragment getJspBody() {
return jspBody;
}
@Override
public void setParent(JspTag parent) {
this.parent = parent;
}
@Override
public JspTag getParent() {
return this.parent;
}
@Override
public void setJspContext(JspContext pc) {
this.jspContext = pc;
}
@Override
public void setJspBody(JspFragment jspBody) {
this.jspBody = jspBody;
}
}
Tag标签的生命周期:
1. 当容器(Tomcat)第一次执行到某个标签时,会创建标签处理类的实例;
2. 然后调用setJspContext(JspContext)方法,把当前JSP页面的pageContext对象传递给这个方法;
3. 如果当前标签有父标签,那么使用父标签的标签处理类对象调用setParent(JspTag)方法;
4. 如果标签有标签体,那么把标签体转换成JspFragment对象,然后调用setJspBody()方法;
5. 每次执行标签时,都调用doTag()方法,它是标签处理方法。
SimpleTagSupport类:
继承SimpleTagSuppport要比实现SimpleTag接口方便太多了,现在你只需要重写doTag()方法和添加自己需要的属性即可,其他方法都已经被SimpleTagSuppport完成了。
WEB容器在处理简单标签的标签体时,会把标签体内容用一个JspFragment对象表示,并调用标签处理器对象的setJspBody方法把JspFragment对象传递给标签处理器对象,JspFragment对象有二个方法:
public abstract class JspFragment {
public abstract void invoke( Writer out ) throws JspException, IOException;
public abstract JspContext getJspContext();
}
public abstract void invoke(Java.io.Writer out)用于执行JspFragment对象所代表的JSP代码片段,参数out用于指定将JspFragment对象的执行结果写入到哪个输出流对象中,如果传递给参数out的值为null,则将执行结果写入到JspContext.getOut()方法返回的输出流对象中。
自定义输出流
@Override
public void doTag() throws JspException, IOException {
System.out.println("doTag");
JspWriter out = this.getJspContext().getOut();
//循环遍历标签体
for(Iterator it = item.iterator();it.hasNext();){
String name = (String) it.next();
this.getJspContext().setAttribute(var, name);
StringWriter sw = new StringWriter();
this.getJspBody().invoke(sw); //body-content为scriptless
String body = sw.toString().toUpperCase();
out.print(body);
}
}
不执行标签下面的页面内容
public class SkipTag extends SimpleTagSupport{
@Override
public void doTag() throws JspException, IOException {
PageContext pageContext = (PageContext) this.getJspContext();
pageContext.getOut().print("<h1>呵呵</h1>");
throw new SkipPageException();
}
}
最后
以上就是鳗鱼火为你收集整理的自定义标签(二)——tld文件方式实现自定义标签TLD文件实现自定义标签简单示例传统标签简单标签的全部内容,希望文章能够帮你解决自定义标签(二)——tld文件方式实现自定义标签TLD文件实现自定义标签简单示例传统标签简单标签所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复